@three-blocks/devtools 3.0.0 → 4.0.0-alpha.314.g989237b8fb54

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/env node
2
+ import {a as a$3,b as b$1}from'./chunk-EE64MV27.js';import {c,b,d,a as a$4}from'./chunk-QJFPNM6W.js';import {a as a$2}from'./chunk-65VEJRRT.js';import {a as a$1,j as j$1,k,e,d as d$1}from'./chunk-5L2VS3BN.js';import {a}from'./chunk-HOZDD6PW.js';import x from'process';import {createServer}from'http';import {createRequire}from'module';import {mkdtemp,rm,lstat,realpath}from'fs/promises';import O from'path';import {tmpdir}from'os';import {pathToFileURL}from'url';import {THREE_WEBGPU_R185_COMPATIBILITY}from'three-blocks/shaders';import {transformThreeWebgpuProviderHooks,transformThreeWebgpuCaptureInstrumentation}from'three-blocks/vite';var ee="three-blocks.shader-capture.runtime.v1";var $;async function I(){$===void 0&&($=(async()=>{let e=createRequire(O.join(process.cwd(),"package.json")).resolve("@three-blocks/devtools/browser/shader-capture"),r=O.resolve(O.dirname(e),"../..");return (await j$1(r,e,a$1.metadata,"Shader capture browser runtime")).data.toString("utf8")})());let t=$;try{return await t}catch(e){throw $===t&&($=void 0),e}}a(I,"loadShaderCaptureRuntimeSource");var we="virtual:three-blocks-shader-capture-client",ge="\0three-blocks:shader-capture-client",te="virtual:three-blocks-shader-capture-runtime",Se="\0three-blocks:shader-capture-runtime";async function ve(){let t=await I(),e=t.replace(/\bexport\{[^{}]*\};?\s*$/u,"");if(e===t)throw new Error("Shader capture browser runtime no longer has the expected final ESM export list.");return e}a(ve,"loadShaderCaptureRuntimeScriptSource");function Je(t){let e=t.split("?",1)[0];return e===void 0?false:e.split(O.sep).join("/").endsWith("/build/three.webgpu.js")}a(Je,"isThreeWebGPUModule");function Te(t){let e=/^(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/u.exec(t);return e?.[1]==="0"&&e[2]==="185"}a(Te,"supportedVersion");function qe(t,e,r={}){if(!Te(e))throw new Error(`Shader provider transform ${THREE_WEBGPU_R185_COMPATIBILITY} supports Three.js 0.185.x; received "${e}".`);let n=transformThreeWebgpuProviderHooks(t,{threeVersion:e,...r.threeRevision===void 0?{}:{threeRevision:r.threeRevision},id:"three/build/three.webgpu.js"}),a=n.state==="already-applied",s=n.code;return r.instrumentCapture&&(s=transformThreeWebgpuCaptureInstrumentation(s,{...r.captureRuntimeImport===void 0?{}:{runtimeImport:r.captureRuntimeImport},...r.forceWebGL===true?{forceWebGL:true}:{}}).code),{code:s,alreadyPresent:a,captureApplied:r.instrumentCapture===true}}a(qe,"applyShaderProviderTransform");var ze=/this\.trackTimestamp\s*=\s*\(\s*parameters\.trackTimestamp\s*===\s*true\s*\);/gu,Ye="Object.defineProperty( this, 'trackTimestamp', { get: () => false, set: () => {} } );";function Re(t,e={}){if(!Te(t))throw new Error(`Shader provider transform ${THREE_WEBGPU_R185_COMPATIBILITY} supports Three.js 0.185.x; received "${t}".`);let r={seen:false,applied:false,alreadyPresent:false};return {plugin:{name:"three-blocks:shader-provider-transform",enforce:"pre",config(){return {optimizeDeps:{exclude:["three","three/webgpu","three/tsl"]},resolve:{dedupe:["three"]}}},resolveId(a){return e.capture&&a===we?ge:e.capture&&a===te?Se:null},async load(a){return e.capture?a===Se?I():a===ge?[`export * from '${te}';`,`import { configureThreeBlocksShaderCapture } from '${te}';`,`configureThreeBlocksShaderCapture( ${JSON.stringify({capture:e.capture.capture,backend:e.capture.forceWebGL===true?"webgl":"webgpu",forceLive:e.capture.forceLive,driven:e.capture.driven,settleFrames:e.capture.settleFrames,settleTimeoutMs:e.capture.settleTimeoutMs,visibilityGuard:e.capture.visibilityGuard,captureId:e.capture.captureId,sessionKey:e.capture.sessionKey,token:e.capture.token,readiness:e.capture.readiness??{},transform:b$1})} );`].join(`
3
+ `):null:null},transform(a,s){if(!Je(s))return null;r.seen=true,r.id=s;try{let o=qe(a,t,{instrumentCapture:e.capture!==void 0,forceWebGL:e.capture?.forceWebGL===!0,...e.capture!==void 0?{captureRuntimeImport:we}:{}}),i=e.disableTimestampQueries===!0?o.code.replace(ze,Ye):o.code;if(e.disableTimestampQueries===!0&&i===o.code)throw new Error("Three.js timestamp tracking anchor drifted; refusing software WebGPU capture.");return r.applied=!0,r.alreadyPresent=o.alreadyPresent,r.captureApplied=o.captureApplied,i===a?null:{code:i,map:null}}catch(o){throw r.error=o instanceof Error?o.message:String(o),o}}},status:r}}a(Re,"createShaderProviderTransform");function ne(t){if(t.error)throw new Error(t.error);if(!t.seen||!t.applied)throw new Error(`The version-gated shader provider transform was not applied (${t.seen?"module seen":"three/webgpu not seen"}).`)}a(ne,"assertShaderProviderTransform");var Be="/__three-blocks/shaders/capture",z="/__three-blocks/shaders/telemetry",xe="/__three-blocks/shaders/runtime.js",Me="/__three-blocks/shaders/bootstrap.js",Ae="/__three-blocks/shaders/bridge.js",Oe=a$1.manifest*2,se=/^[a-z0-9][a-z0-9_-]{0,63}$/u,De=64,je=2e3;async function ar(){try{return (await import('playwright')).chromium}catch(t){throw new Error('Shader browser work requires the optional "playwright" package. Install it in the project and run `npx playwright install chromium`.',{cause:t})}}a(ar,"loadChromium");async function or(t){let e;try{e=createRequire(O.join(t,"package.json")).resolve("vite");}catch(n){throw new Error("Shader capture requires Vite in the target project.",{cause:n})}let r=await import(pathToFileURL(e).href);if(typeof r.createServer!="function")throw new Error(`The target project's Vite entry does not export createServer(): ${e}`);return r.createServer}a(or,"loadProjectCreateServer");var ie=class{static{a(this,"CaptureInbox");}#e=new Map;#r=new Map;wait(e,r){let n=this.#r.get(e);if(n!==void 0)return this.#r.delete(e),Promise.resolve(n);if(this.#e.has(e))throw new Error(`A shader capture is already waiting for scene "${e}".`);return new Promise((a,s)=>{let o=setTimeout(()=>{this.#e.delete(e),s(new Error(`Shader capture "${e}" timed out after ${r}ms.`));},r);this.#e.set(e,{resolve:a,reject:s,timer:o});})}receive(e){if(!a$2(e)||typeof e.scene!="string"||!se.test(e.scene))throw new Error("Capture POST has no safe scene key.");if(!this.#r.has(e.scene)&&!this.#e.has(e.scene)&&this.#r.size>=De)throw new Error("Capture POST exceeds the scene inbox limit.");let r=this.#e.get(e.scene);r?(clearTimeout(r.timer),this.#e.delete(e.scene),r.resolve(e)):this.#r.set(e.scene,e);}cancel(e,r){let n=this.#e.get(e);n&&(clearTimeout(n.timer),this.#e.delete(e),n.reject(r));}rejectAll(e){for(let r of this.#e.values())clearTimeout(r.timer),r.reject(e);this.#e.clear(),this.#r.clear();}};async function sr(t,e){let r=await Promise.race([e.then(n=>({kind:"capture",value:n})),t.then(()=>({kind:"navigation"}))]);return r.kind==="capture"?r.value:e}a(sr,"waitForShaderCaptureAcrossNavigation");var de=class{static{a(this,"TelemetryHub");}#e=new Map;#r=new Map;markFirstRequest(e){se.test(e)&&!this.#r.has(e)&&this.#r.set(e,Date.now());}firstRequest(e){return this.#r.get(e)}receive(e){if(!a$2(e)||typeof e.scene!="string"||!se.test(e.scene))throw new Error("Shader telemetry has no bounded scene key.");if(!this.#e.has(e.scene)&&this.#e.size>=De)throw new Error("Shader telemetry exceeds the scene limit.");this.#e.set(e.scene,e);}read(e){return this.#e.get(e)}clear(e){this.#e.delete(e),this.#r.delete(e);}},ir=a(async(t,e,r,n,a)=>{let s=0;for(;t.__threeBlocksParityUsesAnimationLoop!==true&&s<e;)await n(),s+=1;if(t.__threeBlocksParityUsesAnimationLoop===true){let o=Number(t.__threeBlocksParityCompletedFrame||0),i=Number(r),l=Number.isSafeInteger(i)&&i>=o?i:o+e;for(t.__threeBlocksParityTargetFrame=l;Number(t.__threeBlocksParityCompletedFrame||0)<l;)await n();}return await a(),Number(t.__threeBlocksParityCompletedFrame||0)},"settleShaderParityFrames");function le(t,e){return `(() => {
4
+ const root = globalThis;
5
+ const scene = new URL(location.href).searchParams.get('captureId')
6
+ || 'default';
7
+ const workerCapture = ${JSON.stringify(e??null).replaceAll("<","\\u003c")};
8
+ if (workerCapture) root[Symbol.for('three-blocks.shader-capture.worker-activation.v1')] = { ...workerCapture, scene };
9
+ const randomSeed = (value) => {
10
+ let state = 2166136261;
11
+ for (let index = 0; index < value.length; index++) state = Math.imul(state ^ value.charCodeAt(index), 16777619) >>> 0;
12
+ return state === 0 ? 0x9e3779b9 : state;
13
+ };
14
+ const advanceRandom = (state) => {
15
+ state ^= state << 13;
16
+ state ^= state >>> 17;
17
+ state ^= state << 5;
18
+ return state >>> 0;
19
+ };
20
+ const parityMode = new URL(location.href).searchParams.get('threeBlocksShaderMode');
21
+ if (parityMode === 'live' || parityMode === 'precompiled') {
22
+ root.__threeBlocksParityEpoch = 1700000000000;
23
+ root.__threeBlocksParityTime = 0;
24
+ Date.now = () => root.__threeBlocksParityEpoch + Number(root.__threeBlocksParityTime || 0);
25
+ }
26
+ const randomStreams = new Map();
27
+ Math.random = () => {
28
+ const stackLine = String(new Error().stack || '').split('\\n')[2] || 'unknown';
29
+ const originIndex = stackLine.indexOf(location.origin);
30
+ const callsite = (originIndex >= 0 ? stackLine.slice(0, originIndex) : stackLine).trim() || 'anonymous';
31
+ const next = advanceRandom(randomStreams.get(callsite) ?? randomSeed(scene + '|' + callsite));
32
+ randomStreams.set(callsite, next);
33
+ return next / 4294967296;
34
+ };
35
+ const readiness = { workerReady: false, assetsReady: false, textReady: false, compileEnd: false, firstFrame: false };
36
+ const stats = { buildCalls: 0, renderBuilds: 0, computeBuilds: 0, registeredBuilds: 0, registeredRenderBuilds: 0, registeredComputeBuilds: 0, buildMs: 0, syncBuildMs: 0, asyncBuildElapsedMs: 0, precompiledSetupMs: 0, maxSyncBuildMs: 0, maxPrecompiledSetupMs: 0, hydrationMs: 0, maxHydrationMs: 0, injected: 0, injectedRender: 0, injectedCompute: 0, missed: 0, missedRender: 0, missedCompute: 0, missedKeys: [] };
37
+ const settleParityFrames = ${ir.toString()};
38
+ const settle = (frames, requestedTarget) => settleParityFrames(
39
+ root,
40
+ frames,
41
+ requestedTarget,
42
+ () => new Promise((resolve) => requestAnimationFrame(resolve)),
43
+ () => new Promise((resolve) => setTimeout(resolve, 100)),
44
+ );
45
+ const bridge = { protocolVersion: 1, compatibility: ${JSON.stringify(t)}, transform: ${b$1}, readiness, stats, settle };
46
+ root.__threeBlocksShaderTools = bridge;
47
+ requestAnimationFrame(() => requestAnimationFrame(() => { readiness.firstFrame = true; }));
48
+ const poll = async () => {
49
+ try {
50
+ const response = await fetch('/__three-blocks/shaders/telemetry?scene=' + encodeURIComponent(scene), { cache: 'no-store' });
51
+ if (response.status !== 204 && response.ok) {
52
+ const payload = await response.json();
53
+ if (payload.protocolVersion !== 1 || payload.compatibility !== bridge.compatibility || payload.transform !== bridge.transform) throw new Error('incompatible shader telemetry');
54
+ Object.assign(stats, payload.stats);
55
+ if (Array.isArray(payload.demands)) bridge.demands = payload.demands;
56
+ if (typeof payload.demandTotal === 'number') bridge.demandTotal = payload.demandTotal;
57
+ readiness.workerReady = true;
58
+ if (payload.settled === true) {
59
+ readiness.assetsReady = true;
60
+ readiness.textReady = true;
61
+ readiness.compileEnd = true;
62
+ }
63
+ const smoke = root.__threeBlocksSmoke && typeof root.__threeBlocksSmoke === 'object' ? root.__threeBlocksSmoke : { protocolVersion: 1, worker: { offscreenCanvas: true, visible: true }, readiness: {} };
64
+ smoke.readiness = { ...(smoke.readiness || {}), ...readiness };
65
+ root.__threeBlocksSmoke = smoke;
66
+ }
67
+ } catch {}
68
+ setTimeout(poll, 40);
69
+ };
70
+ void poll();
71
+ })();`}a(le,"publicBridgeScript");function Le(t,e,r,n,a=false){let s=new URL(t.url??"/","http://three-blocks.invalid"),o=s.pathname;if(a&&t.method==="GET"&&/^\/three-blocks\/shaders\/[a-z0-9][a-z0-9_-]{0,63}\.(?:webgpu|webgl)\.json$/u.test(o))return e.statusCode=204,e.end(),true;if(o===z&&t.method==="GET"){let h=s.searchParams.get("scene")??"",S=n.read(h);return S===void 0?(e.statusCode=204,e.end()):(e.statusCode=200,e.setHeader("Content-Type","application/json"),e.end(JSON.stringify(S))),true}let i=o===Be,l=o===z;if(!i&&!l)return false;if(t.method!=="POST")return e.statusCode=405,e.end("POST required"),true;let c=[],y=0,p=false;return t.on("data",h=>{y+=h.byteLength,y>Oe?p=true:c.push(h);}),t.on("end",()=>{if(p){e.statusCode=413,e.end("capture payload too large");return}try{let h=JSON.parse(Buffer.concat(c).toString("utf8"));l?n.receive(h):r.receive(h),e.statusCode=204,e.end();}catch(h){e.statusCode=400,e.end(h instanceof Error?h.message:String(h));}}),true}a(Le,"handleCaptureEndpoint");function dr(t,e,r,n,a){return {name:"three-blocks:shader-capture-endpoint",transformIndexHtml(){return [{tag:"script",children:le(n,a),injectTo:"head-prepend"}]},configureServer(s){s.middlewares.use((o,i,l)=>{let y=new URL(o.url??"/","http://three-blocks.invalid").searchParams.get("captureId");y!==null&&e.markFirstRequest(y),!Le(o,i,t,e,r)&&l();});}}}a(dr,"captureEndpointPlugin");function Ee(t,e,r){let n=new URL(e.url,t);for(let[a,s]of Object.entries(r))n.searchParams.set(a,s);return n.href}a(Ee,"browserUrl");function lr(t,e,r){let n=new URL(e.url,t);for(let a of ["capture","precompiled"])n.searchParams.delete(a);return n.searchParams.set("captureId",e.key),n.searchParams.set("threeBlocksShaderCapture","1"),n.searchParams.set("threeBlocksShaderMode","precompiled"),r==="webgl"&&n.searchParams.set("webgl","1"),n.searchParams.set("tbStats","1"),n.href}a(lr,"publishedBrowserUrl");async function A(t){let e;try{await Promise.race([t(),new Promise(r=>{e=setTimeout(r,je);})]);}catch{}finally{e&&clearTimeout(e);}}a(A,"closeBrowserResource");async function q(t){let e;try{return await Promise.race([t().catch(()=>null),new Promise(r=>{e=setTimeout(()=>r(null),je);})])}finally{e!==void 0&&clearTimeout(e);}}a(q,"readPageDiagnostic");function ae(t){let e=a(()=>{},"resolveFirst"),r=new Promise(o=>{e=o;}),n={console:[],page:[],first:r},a$1=false,s=a(o=>{a$1||(a$1=true,e(new Error(o)));},"report");return t.on("console",o=>{let i=o.type()==="warning"&&/\[three-blocks\/shaders\].*(?:has no entry|manifest miss|hydration)/iu.test(o.text());if(o.type()!=="error"&&!i||a$4(o.text()))return;let l=o.location().url,c=l.length>0?`${o.text()} (${l})`:o.text();n.console.push(c),s(`Browser console ${i?"warning":"error"}: ${c}`);}),t.on("pageerror",o=>{if(a$4(o.message))return;let i=o.stack||o.message;n.page.push(i),s(`Browser page error: ${i}`);}),t.on("crash",()=>s("Browser page crashed before shader capture completed.")),t.on("close",()=>s("Browser page closed before shader capture completed.")),n}a(ae,"collectErrors");function N(t,e,r=false){return typeof t[e]=="boolean"?t[e]:r}a(N,"booleanField");function ur(t){if(!a$2(t))throw new Error("Browser shader stats are unavailable.");let e=a((r,n=true)=>{let a=t[r];if(typeof a!="number"||!Number.isFinite(a)||a<0||n&&!Number.isSafeInteger(a))throw new Error(`Browser shader stats.${r} is invalid.`);return a},"read");return {buildCalls:e("buildCalls"),renderBuilds:e("renderBuilds"),computeBuilds:e("computeBuilds"),registeredBuilds:e("registeredBuilds"),registeredRenderBuilds:e("registeredRenderBuilds"),registeredComputeBuilds:e("registeredComputeBuilds"),buildMs:e("buildMs",false),syncBuildMs:e("syncBuildMs",false),asyncBuildElapsedMs:e("asyncBuildElapsedMs",false),precompiledSetupMs:e("precompiledSetupMs",false),maxSyncBuildMs:e("maxSyncBuildMs",false),maxPrecompiledSetupMs:e("maxPrecompiledSetupMs",false),hydrationMs:e("hydrationMs",false),maxHydrationMs:e("maxHydrationMs",false),injected:e("injected"),injectedRender:e("injectedRender"),injectedCompute:e("injectedCompute"),missed:e("missed"),missedRender:e("missedRender"),missedCompute:e("missedCompute"),...Array.isArray(t.missedKeys)&&t.missedKeys.every(r=>typeof r=="string")?{missedKeys:t.missedKeys}:{}}}a(ur,"parseStats");function K(t,e,r,n,a){if(!a$2(t))throw new Error("The page did not expose the public shader devtools bridge.");if(!r&&t.protocolVersion!==1)throw new Error(`Scene "${e.key}" exposed an unsupported shader bridge protocol.`);let s=t.readiness;if(!a$2(s))throw new Error("The shader bridge readiness snapshot is invalid.");let o={workerReady:N(s,"workerReady"),assetsReady:N(s,"assetsReady"),textReady:e.requiresText?N(s,"textReady"):true,compileEnd:N(s,"compileEnd"),firstFrame:N(s,"firstFrame",r)};if(!o.workerReady||!o.assetsReady||!o.textReady||!o.compileEnd)throw new Error(`Scene "${e.key}" did not reach worker/assets/text/compile readiness.`);let i=r?a:t.compatibility,l=r?b$1:t.transform;if(i!==a||l!==b$1)throw new Error(`Scene "${e.key}" exposed incompatible shader bridge metadata.`);ne(n);let c=Array.isArray(t.demands)?t.demands.filter(a$2).slice(0,64):void 0;return {compatibility:a,transform:l,readiness:o,stats:ur(t.stats),...c===void 0?{}:{demands:c}}}a(K,"parseBridge");async function J(t,e){return t.evaluate(r=>{let n=globalThis;if(!r)return n.__threeBlocksShaderTools??null;let a=n.__threeBlocks,s=n.__threeBlocksTslStats;if(!a||typeof a!="object")return null;let o=a;return {readiness:{workerReady:o.workerReady===true,assetsReady:o.loadEnd===true||o.assetsReady===true,textReady:o.textReady===true,compileEnd:o.compileEnd===true,firstFrame:true},stats:s}},e)}a(J,"readBridge");async function Ie(t,e){let r=await q(()=>t.evaluate(n=>{let a$1=globalThis,s=a(i=>{let l=a$1;for(let c of i.split(".")){if(l===null||typeof l!="object"&&typeof l!="function")return;l=Reflect.get(l,c);}return l},"resolveGlobal"),o=null;if(n!==null)try{o=JSON.stringify(s(`${n}Diagnostics`))??null;}catch{o=null;}return JSON.stringify({parityCompletedFrame:Number(a$1.__threeBlocksParityCompletedFrame??0),diagnostics:o})},e??null));return r===null?{alive:false,signature:""}:{alive:true,signature:r}}a(Ie,"probePageProgress");async function U(t,e,r,n){let a$1=t.evaluate(async c=>{let p=globalThis.__threeBlocksShaderTools;if(p&&typeof p=="object"){let g=Reflect.get(p,"settle");if(typeof g=="function"){let u=await g(c.frames,c.targetFrame);return typeof u=="number"&&Number.isSafeInteger(u)?u:0}}if(!c.legacy||!c.modulePath)throw new Error("The public shader bridge does not provide deterministic settle().");let h=await import(c.modulePath);if(!h.store||typeof h.store!="object")throw new Error("Legacy deterministic store is unavailable.");let S=h.store;return S.recordFrameCount=0,S.recordFrameRate=60,S.recordTotalFrames=c.frames,S.recording=true,await new Promise((g,u)=>{let f=Date.now(),m=a(()=>{if(S.recording===false)return g();if(Date.now()-f>c.timeoutMs)return u(new Error("Legacy deterministic settle timed out."));setTimeout(m,25);},"poll");m();}),0},{frames:r,targetFrame:n,legacy:e.bridge.legacyLab,modulePath:e.bridge.deterministicModule??null,timeoutMs:e.timeoutMs}),s=j(e),o=false,i,l=(async()=>{let c=Date.now(),y=c,p="";for(;;){let h=s-(Date.now()-c);if(h<=0)throw new Error(`Shader parity settle timed out after ${s}ms.`);let S=e.timeoutMs-(Date.now()-y);if(S<=0){let g=await Ie(t,e.readiness.global);if(o)throw new Error("Shader parity settle already resolved.");if(g.alive&&g.signature===p)throw new Error(`Shader parity settle timed out after ${e.timeoutMs}ms without progress (hard cap ${s}ms).`);p=g.signature,y=Date.now();continue}if(await new Promise(g=>{i=setTimeout(g,Math.min(S,h,2500));}),o)throw new Error("Shader parity settle already resolved.")}})();try{return await Promise.race([a$1,l])}finally{o=true,i!==void 0&&clearTimeout(i),l.catch(()=>{});}}a(U,"settlePage");async function ke(t,e,r){e.readiness.selector===void 0&&e.readiness.global===void 0||await Promise.race([t.waitForFunction(n=>{let a=globalThis;if(n.selector!==void 0&&a.document?.querySelector(n.selector)==null)return false;if(n.global===void 0)return true;let s=a;for(let l of n.global.split(".")){if((typeof s!="object"||s===null)&&typeof s!="function")return false;s=Reflect.get(s,l);}if(s===true)return true;if(typeof s!="object"||s===null||typeof Reflect.get(s,"then")!="function")return false;let o=a.__threeBlocksReadinessState,i=o instanceof Map?o:new Map;return o instanceof Map||(a.__threeBlocksReadinessState=i),i.has(s)||(i.set(s,false),Promise.resolve(s).then(()=>i.set(s,true),()=>i.set(s,false))),i.get(s)===true},e.readiness,{timeout:j(e)}),r.first.then(n=>Promise.reject(n))]);}a(ke,"waitForConfiguredReadiness");async function Ce(t){let e=t.locator("canvas"),n=(await e.evaluateAll(a=>a.map((s,o)=>{let i=s.getBoundingClientRect();return {index:o,area:Math.max(0,i.width)*Math.max(0,i.height)}}))).sort((a,s)=>s.area-a.area)[0];if(n===void 0||n.area<=0)throw new Error("Published page has no visible renderer canvas.");return Buffer.from(await e.nth(n.index).screenshot({type:"png"}))}a(Ce,"primaryCanvasScreenshot");async function oe(t,e,r,n,a,s,o){let i=Y(n,a,s),l=await ve(),c=`(() => {
72
+ const runtime = globalThis[Symbol.for(${JSON.stringify(ee)})];
73
+ if (!runtime || typeof runtime.configureThreeBlocksShaderCapture !== 'function') {
74
+ throw new Error('@three-blocks/devtools attached shader capture runtime is incompatible.');
75
+ }
76
+ runtime.configureThreeBlocksShaderCapture(${JSON.stringify(i).replaceAll("<","\\u003c")});
77
+ })();`;await t.addInitScript({content:`${l}
78
+ ${c}`}),await t.addInitScript({content:le(o,i)}),await t.route("**/*",async y=>{let p=y.request(),h=new URL(p.url()),S=h.pathname;if(p.method()==="GET"&&n!=="precompiled"&&/^\/three-blocks\/shaders\/[a-z0-9][a-z0-9_-]{0,63}\.(?:webgpu|webgl)\.json$/u.test(S)){await y.fulfill({status:204});return}if(S===z&&p.method()==="GET"){let f=r.read(h.searchParams.get("scene")??"");await y.fulfill(f===void 0?{status:204}:{status:200,contentType:"application/json",body:JSON.stringify(f)});return}let g=S===Be,u=S===z;if((g||u)&&p.method()==="POST"){let f=p.postDataBuffer()??Buffer.alloc(0);if(f.byteLength>Oe){await y.fulfill({status:413,body:"capture payload too large"});return}try{let m=JSON.parse(f.toString("utf8"));u?r.receive(m):e.receive(m),await y.fulfill({status:204});}catch(m){await y.fulfill({status:400,body:m instanceof Error?m.message:String(m)});}return}await y.continue();});}a(oe,"configureAttachedContext");var cr=Object.freeze({".avif":"image/avif",".css":"text/css; charset=utf-8",".gif":"image/gif",".html":"text/html; charset=utf-8",".ico":"image/x-icon",".jpeg":"image/jpeg",".jpg":"image/jpeg",".js":"text/javascript; charset=utf-8",".json":"application/json; charset=utf-8",".mjs":"text/javascript; charset=utf-8",".mp4":"video/mp4",".png":"image/png",".svg":"image/svg+xml",".wasm":"application/wasm",".webm":"video/webm",".webp":"image/webp",".woff":"font/woff",".woff2":"font/woff2"});function hr(t){let e=[`<script src="${Ae}"></script>`,`<script type="module" src="${Me}"></script>`].join(""),r=/<head(?:\s[^>]*)?>/iu.exec(t);return r===null?`${e}${t}`:`${t.slice(0,r.index+r[0].length)}${e}${t.slice(r.index+r[0].length)}`}a(hr,"injectStaticShaderCaptureHtml");function mr(t,e,r){let n=Y(t,e,r);return [`import { configureThreeBlocksShaderCapture } from ${JSON.stringify(xe)};`,`configureThreeBlocksShaderCapture( ${JSON.stringify(n).replaceAll("<","\\u003c")} );`].join(`
79
+ `)}a(mr,"staticCaptureBootstrap");var pr=5e3;function j(t){return Math.max(t.maxTimeoutMs??t.timeoutMs,t.timeoutMs)}a(j,"configuredHardCapMs");function Y(t,e,r){return {capture:t==="capture",backend:r,driven:true,forceLive:t!=="precompiled",settleFrames:e.settleFrames,settleTimeoutMs:e.settleTimeoutMs??3e4,...t==="capture"?{deadlineAt:Date.now()+Math.max(1e3,j(e)-pr)}:{},readiness:e.readiness,transform:b$1}}a(Y,"shaderCaptureConfiguration");async function fr(t,e){let r=await k(t,e,o=>/\.(?:c|m)?js$/u.test(o),16384),n=false,a=false,s;for(let o of r){let i=(await j$1(t,o,a$1.texture,"Static application JavaScript")).data.toString("utf8");if(i.includes("nodeBuilderStateProvider")&&(n=true),i.includes("three-blocks.shader-capture.runtime.v1")&&(a=true,s??=o),n&&a)break}if(!n||!a)throw new Error("The static build does not contain the Three.js shader capture transform. Run `shaders capture` without an explicit driver or URL to preview adapter wiring and build automatically, or add the manual adapter from the package README and rebuild.");return {seen:true,applied:true,alreadyPresent:false,captureApplied:true,...s===void 0?{}:{id:s}}}a(fr,"inspectStaticCaptureTransform");async function yr(t,e$1){let r;try{r=decodeURIComponent(e$1);}catch{return}if(r.includes("\0"))return;let n=r.replace(/^\/+/u,""),a=O.resolve(t,n.length===0?"index.html":n);if(!e(t,a)||a===t||(await d$1(a)&&(await lstat(a)).isDirectory()&&(a=O.join(a,"index.html")),!await d$1(a)&&O.extname(a).length===0&&(a=O.join(t,"index.html")),!await d$1(a)))return;let s=await j$1(t,a,a$1.texture,"Static application asset");return {data:s.data,file:s.path}}a(yr,"staticRequestFile");async function wr(t,e$1,r,n,a,s){let o=await realpath(t.root),i=await realpath(O.resolve(o,t.serve));if(!e(o,i)||i===o)throw new Error("--serve must name a built application directory inside the project root.");let l=await fr(o,i),c=await I(),y=mr(e$1,n,r),p=le(a$3(t.runtime.three,r),Y(e$1,n,r)),h=createServer((g,u)=>{(async()=>{if(Le(g,u,a,s,e$1!=="precompiled"))return;let f=new URL(g.url??"/","http://three-blocks.invalid");if(g.method!=="GET"&&g.method!=="HEAD"){u.statusCode=405,u.end("GET or HEAD required");return}let m=f.pathname===xe?c:f.pathname===Me?y:f.pathname===Ae?p:void 0;if(m!==void 0){u.statusCode=200,u.setHeader("Content-Type","text/javascript; charset=utf-8"),u.setHeader("Cache-Control","no-store"),u.end(g.method==="HEAD"?void 0:m);return}let w=await yr(i,f.pathname);if(w===void 0){u.statusCode=404,u.end("Not found");return}let v=O.extname(w.file).toLowerCase(),b=v===".html"?Buffer.from(hr(w.data.toString("utf8"))):w.data;u.statusCode=200,u.setHeader("Content-Type",cr[v]??"application/octet-stream"),u.setHeader("Content-Length",String(b.byteLength)),u.setHeader("Cache-Control","no-store"),u.end(g.method==="HEAD"?void 0:b);})().catch(f=>{if(u.headersSent)return u.destroy(f instanceof Error?f:void 0);u.statusCode=500,u.end(f instanceof Error?f.message:String(f));});});await new Promise((g,u)=>{h.once("error",u),h.listen(0,"127.0.0.1",()=>{h.off("error",u),g();});});let S=h.address();if(!S||typeof S=="string")throw new Error("Static capture server did not expose an ephemeral TCP address.");return {server:h,baseUrl:`http://127.0.0.1:${S.port}`,transform:l}}a(wr,"startStaticShaderServer");function gr(t,e,r){return t===e?true:r&&t!==void 0&&t!=="capture"&&e!=="capture"}a(gr,"canReuseShaderBrowserSession");function _e(t){let e=t instanceof Error?t.message:String(t);return e.includes("Execution context was destroyed")&&e.includes("navigation")||/^launch: Timeout \d+ms exceeded\./u.test(e)}a(_e,"isRetryableShaderObservationNavigation");var H=class{static{a(this,"PlaywrightShaderDriver");}#e;#r=new ie;#n=new de;#h;#m;#s;#i;#t;#a;#p;#l;#f;#y;#u=0;#c;constructor(e){this.#e=e;}#d(){return this.#h?.status??this.#m??{seen:false,applied:false,alreadyPresent:false}}async#w(e,r){if(this.#t?.isConnected()===true)return;this.#t=void 0;let n=await ar();this.#c??=await mkdtemp(O.join(tmpdir(),"three-blocks-shader-cache-")),this.#t=await n.launch({headless:r==="webgl"?this.#e.headed!==true:d(this.#e.headed===true),args:[...r==="webgl"?c():b(),`--disk-cache-dir=${this.#c}`],timeout:e.timeoutMs}),this.#u=0;}async#o(e,r,n){let a;try{return await Promise.race([n(),new Promise((s,o)=>{a=setTimeout(()=>o(new Error(`launch: Timeout ${r.timeoutMs}ms exceeded.`)),r.timeoutMs);})])}catch(s){throw this.#t===e&&(this.#t=void 0),await A(()=>e.close()),s}finally{a!==void 0&&clearTimeout(a);}}async#g(e,r,n){let a=this.#s!==void 0||this.#i!==void 0||this.#e.url!==void 0&&this.#a!==void 0,s=this.#f===r&&(this.#i!==void 0?this.#l===e:gr(this.#l,e,n.bridge.legacyLab));if(a&&s){this.#l=e,await this.#w(n,r);return}if(a&&await this.close(),this.#l=e,this.#f=r,this.#e.url!==void 0){let o=new URL(this.#e.url);if(!["http:","https:"].includes(o.protocol)||o.username||o.password||o.hash)throw new Error("Attached shader URL must be an HTTP(S) URL without credentials or a fragment.");this.#a=o.href,this.#m={seen:true,applied:true,alreadyPresent:false,captureApplied:true,id:"attached-application"},await this.#w(n,r);return}if(this.#e.serve!==void 0)try{let o=await wr({root:this.#e.root,runtime:this.#e.runtime,serve:this.#e.serve,...this.#e.headed===void 0?{}:{headed:this.#e.headed}},e,r,n,this.#r,this.#n);this.#i=o.server,this.#m=o.transform,this.#a=o.baseUrl,await this.#w(n,r);return}catch(o){throw await this.close(),o}this.#h=Re(this.#e.runtime.three,{disableTimestampQueries:x.env.THREE_BLOCKS_SOFTWARE_WEBGPU==="1",...n.bridge.legacyLab?{}:{capture:{capture:e==="capture",driven:true,forceLive:e!=="precompiled",forceWebGL:r==="webgl",settleFrames:n.settleFrames,settleTimeoutMs:n.settleTimeoutMs??3e4,readiness:n.readiness}}}),this.#p={THREE_BLOCKS_SHADER_TOOL:x.env.THREE_BLOCKS_SHADER_TOOL,THREE_BLOCKS_PRECOMPILE_CAPTURE_SERVER:x.env.THREE_BLOCKS_PRECOMPILE_CAPTURE_SERVER},x.env.THREE_BLOCKS_SHADER_TOOL="1",x.env.THREE_BLOCKS_PRECOMPILE_CAPTURE_SERVER="1";try{let o=await or(this.#e.root);this.#s=await o({root:this.#e.root,logLevel:"error",plugins:[this.#h.plugin,dr(this.#r,this.#n,e!=="precompiled",a$3(this.#e.runtime.three,r),Y(e,n,r))],server:{host:"127.0.0.1",port:0}}),await this.#s.listen();let i=this.#s.httpServer?.address();if(!i||typeof i=="string")throw new Error("Vite did not expose an ephemeral TCP address.");this.#a=`http://127.0.0.1:${i.port}`,await this.#w(n,r);}catch(o){throw await this.close(),o}}async capture(e,r,n="webgpu"){let a$1=Date.now();if(await this.#g("capture",n,r),!this.#t||!this.#a)throw new Error("Shader browser driver did not start.");let s=this.#t,o=await this.#o(s,r,()=>s.newContext({viewport:r.viewport}));this.#e.url!==void 0&&await this.#o(s,r,()=>oe(o,this.#r,this.#n,"capture",r,n,a$3(this.#e.runtime.three,n)));let i=await this.#o(s,r,()=>o.newPage()),l=ae(i);this.#n.clear(e.key);let c=j(r),y=this.#r.wait(e.key,c),p=false;c>r.timeoutMs?(async()=>{let m=Math.min(5e3,Math.max(1e3,Math.round(r.timeoutMs/8))),w=Date.now(),v="",b="";for(;!p;){if(await new Promise(P=>setTimeout(P,m)),p)return;let R=this.#n.read(e.key),_=R===void 0?"":JSON.stringify(R);if(_!==v){v=_,w=Date.now();continue}if(Date.now()-w<r.timeoutMs)continue;let E=await Ie(i,r.readiness.global);if(p)return;if(!E.alive||E.signature!==b){b=E.signature,w=Date.now();continue}this.#r.cancel(e.key,new Error(`Shader capture "${e.key}" timed out after ${r.timeoutMs}ms without progress (hard cap ${c}ms).`));return}})().catch(()=>{}):void 0;let S=Promise.race([y,l.first.then(m=>Promise.reject(m))]),g=a(()=>{},"resolveDisconnected"),u=new Promise(m=>{g=m;}),f=a(()=>g(new Error("Shader capture browser disconnected before posting a manifest.")),"onDisconnected");s.on("disconnected",f);try{let m=i.goto(Ee(this.#a,e,{threeBlocksShaderCapture:"1",capture:"1",captureId:e.key,captureFrames:String(Math.max(4,r.settleFrames)),settleFrames:String(r.settleFrames),...n==="webgl"?{webgl:"1"}:{}}),{waitUntil:"domcontentloaded",timeout:r.timeoutMs}),w=await sr(m,Promise.race([S,u.then(b=>Promise.reject(b))]));ne(this.#d());let v=[...l.page,...l.console];if(v.length>0)throw new Error(`Capture browser errors:
80
+ - ${v.join(`
81
+ - `)}`);return a$2(w)?{...w,timing:{...a$2(w.timing)?w.timing:{},launch:a$1,...this.#n.firstRequest(e.key)===void 0?{}:{firstRequest:this.#n.firstRequest(e.key)},posted:Date.now()}}:w}catch(m){let w=m instanceof Error?m:new Error(String(m));this.#r.cancel(e.key,w),await y.catch(()=>{});let v=this.#n.read(e.key);if(w.message.includes("timed out")){let b=await q(()=>i.evaluate(E=>{let P=globalThis,G=P.__threeBlocksShaderTools,Fe=G&&typeof G=="object"?G:void 0,$e=P.document,M=Reflect.get(P,Symbol.for("three-blocks.shader-capture.cache-value.v1")),W=P.location,Ne=W!==null&&typeof W=="object"&&typeof Reflect.get(W,"href")=="string"?String(Reflect.get(W,"href")):"http://three-blocks.invalid/",Ue=new URL(Ne).searchParams.get("captureId")??"default",ue=M!==null&&typeof M=="object"&&typeof Reflect.get(M,"registrations")=="function"?Reflect.apply(Reflect.get(M,"registrations"),M,[Ue]):void 0,ce=a(T=>{let B=P;for(let He of T.split(".")){if(B===null||typeof B!="object"&&typeof B!="function")return;B=Reflect.get(B,He);}return B},"resolveGlobal"),he=null,Q=null;if(E!==null){let T=ce(E);he=T===true||T===false?T:T===void 0?"absent":T!==null&&typeof T.then=="function"?"promise":typeof T;try{let B=JSON.stringify(ce(`${E}Diagnostics`));Q=typeof B=="string"?B.slice(0,2048):null;}catch{Q=null;}}return {documentReadyState:$e?.readyState??null,parityTargetFrame:Number(P.__threeBlocksParityTargetFrame??0),parityCompletedFrame:Number(P.__threeBlocksParityCompletedFrame??0),readiness:Fe?.readiness??null,readinessGlobal:he,readinessDiagnostics:Q,captureCache:M!==null&&typeof M=="object"?{activeScene:Reflect.get(M,"activeScene")??null,registrations:Array.isArray(ue)?ue.slice(0,16).map(T=>T!==null&&typeof T=="object"?{key:Reflect.get(T,"key")??null,kind:Reflect.get(T,"kind")??null}:null):null}:null,threeRuntimeResources:performance.getEntriesByType("resource").map(T=>T.name).filter(T=>T.includes("three.webgpu"))}},r.readiness.global??null)),R=r.readiness.global!==void 0&&b!==null&&b.readinessGlobal!==true,_=b===null?"Capture page did not answer diagnostics before the deadline; its main thread was blocked or gone. ":R?`Application readiness "${r.readiness.global}" never completed (state: ${JSON.stringify(b.readinessGlobal)}${b.readinessDiagnostics===null?"":`, diagnostics: ${b.readinessDiagnostics}`}). `:"";throw new Error(`${_}${w.message} Last capture state for "${e.key}": ${JSON.stringify({page:b,telemetry:v??null,transform:this.#d()})}`)}throw w}finally{p=true,s.off("disconnected",f),await A(()=>o.close()),this.#u++,(!s.isConnected()||this.#u>=(r.browserRelaunchScenes??8))&&(this.#t===s&&(this.#t=void 0),await A(()=>s.close()),this.#u=0);}}async observe(e,r,n,a,s="webgpu"){for(let o=0;o<2;o++)try{return await this.#S(e,s,r,n,a)}catch(i){if(o===0&&_e(i))continue;throw i}throw new Error(`Shader observation retry was exhausted for scene "${e.key}".`)}async#S(e,r,n,a,s){if(await this.#g(n,r,a),!this.#t||!this.#a)throw new Error("Shader browser driver did not start.");let o=this.#t,i=await this.#o(o,a,()=>o.newContext({viewport:a.viewport}));this.#e.url!==void 0&&await this.#o(o,a,()=>oe(i,this.#r,this.#n,n,a,r,a$3(this.#e.runtime.three,r)));let l=await this.#o(o,a,()=>i.newPage()),c=ae(l);this.#n.clear(e.key);try{await l.goto(Ee(this.#a,e,{threeBlocksShaderCapture:"1",threeBlocksShaderMode:n,captureId:e.key,...n==="live"?{precompiled:"0"}:{},...r==="webgl"?{webgl:"1"}:{}}),{waitUntil:"domcontentloaded",timeout:a.timeoutMs}),await Promise.race([l.waitForFunction(a.bridge.legacyLab?"globalThis.__threeBlocks?.compileEnd === true":"globalThis.__threeBlocksShaderTools?.readiness?.compileEnd === true",void 0,{timeout:j(a)}),c.first.then(u=>Promise.reject(u))]),K(await J(l,a.bridge.legacyLab),e,a.bridge.legacyLab,this.#d(),a$3(this.#e.runtime.three,r)),await ke(l,a,c),await Promise.race([l.waitForFunction(a.bridge.legacyLab?"true":"globalThis.__threeBlocksShaderTools?.readiness?.firstFrame === true",void 0,{timeout:j(a)}),c.first.then(u=>Promise.reject(u))]),await U(l,a,1);let y=Buffer.from(await l.screenshot({type:"png",timeout:a.timeoutMs})),p=e.parityFrames??a.parity.frames,h=await U(l,a,p,s),S=Buffer.from(await l.screenshot({type:"png",timeout:a.timeoutMs}));e.parityFrames!==void 0&&p<a.parity.frames&&await U(l,a,a.parity.frames-p);let g=K(await J(l,a.bridge.legacyLab),e,a.bridge.legacyLab,this.#d(),a$3(this.#e.runtime.three,r));return this.#y===void 0&&(this.#y=await l.evaluate(async u=>{if(u==="webgl"){let E=Reflect.get(globalThis,"document").querySelector("canvas")?.getContext("webgl2"),P=E?.getExtension("WEBGL_debug_renderer_info");return {adapter:E&&P?String(E.getParameter(P.UNMASKED_RENDERER_WEBGL)):"WebGL adapter",platform:navigator.platform||navigator.userAgent}}let f=Reflect.get(navigator,"gpu"),m=typeof f=="object"&&f!==null?Reflect.get(f,"requestAdapter"):void 0,w=typeof m=="function"?await Reflect.apply(m,f,[]):void 0,v=typeof w=="object"&&w!==null?Reflect.get(w,"info"):void 0,b=["description","device","architecture","vendor"].flatMap(R=>{let _=typeof v=="object"&&v!==null?Reflect.get(v,R):void 0;return typeof _=="string"&&_.length>0?[_]:[]});return {adapter:[...new Set(b)].join(" \xB7 ")||"WebGPU adapter",platform:navigator.platform||navigator.userAgent}},r)),{scene:e.key,mode:n,...g,frame:h,firstFrameScreenshot:y,screenshot:S,errors:[...c.page,...c.console]}}catch(y){let p=y instanceof Error?y:new Error(String(y));if(/(?:timed?\s*out|timeout)/iu.test(p.message)){let u=this.#n.read(e.key),f=await q(()=>l.evaluate(()=>{let m=globalThis,w=m.__threeBlocksShaderTools,v=w&&typeof w=="object"?w:void 0;return {documentReadyState:m.document?.readyState??null,parityTargetFrame:Number(m.__threeBlocksParityTargetFrame??0),parityCompletedFrame:Number(m.__threeBlocksParityCompletedFrame??0),readiness:v?.readiness??null,threeRuntimeResources:performance.getEntriesByType("resource").map(R=>R.name).filter(R=>R.includes("three.webgpu"))}}));throw new Error(`${p.message} Last ${n} state for "${e.key}": ${JSON.stringify({page:f,telemetry:u??null})}`)}let h=this.#n.read(e.key),g=(a$2(h)&&Array.isArray(h.demands)&&h.demands.length>0?JSON.stringify(h.demands).slice(0,6e3):void 0)??await q(()=>l.evaluate(u=>{let f=globalThis,m=Reflect.get(f,Symbol.for(u)),w=m!==null&&typeof m=="object"?Reflect.get(m,"readThreeBlocksShaderDemands"):void 0,v=typeof w=="function"?Reflect.apply(w,m,[]):void 0,b=f.__threeBlocksShaderTools,R=Array.isArray(v)&&v.length>0?v:b&&typeof b=="object"?Reflect.get(b,"demands"):void 0;return Array.isArray(R)&&R.length>0?JSON.stringify(R).slice(0,6e3):null},ee));throw typeof g=="string"?new Error(`${p.message}
82
+ ${n} demands: ${g}`):p}finally{await A(()=>i.close());}}async observePublished(e,r,n="webgpu"){for(let a=0;a<2;a++)try{return await this.#b(e,n,r)}catch(s){if(a===0&&_e(s))continue;throw s}throw new Error(`Published shader observation retry was exhausted for scene "${e.key}".`)}async#b(e,r,n){if(await this.#g("precompiled",r,n),!this.#t||!this.#a)throw new Error("Shader browser driver did not start.");let a=this.#t,s=await this.#o(a,n,()=>a.newContext({viewport:n.viewport}));this.#e.url!==void 0&&await this.#o(a,n,()=>oe(s,this.#r,this.#n,"precompiled",n,r,a$3(this.#e.runtime.three,r)));let o=await this.#o(a,n,()=>s.newPage()),i=ae(o);this.#n.clear(e.key);try{await o.goto(lr(this.#a,e,r),{waitUntil:"domcontentloaded",timeout:n.timeoutMs}),await Promise.race([o.waitForFunction(n.bridge.legacyLab?"globalThis.__threeBlocks?.compileEnd === true":"globalThis.__threeBlocksShaderTools?.readiness?.compileEnd === true",void 0,{timeout:j(n)}),i.first.then(h=>Promise.reject(h))]),await ke(o,n,i),K(await J(o,n.bridge.legacyLab),e,n.bridge.legacyLab,this.#d(),a$3(this.#e.runtime.three,r)),await U(o,n,2);let l=await Ce(o),c=await U(o,n,120),y=await Ce(o),p=K(await J(o,n.bridge.legacyLab),e,n.bridge.legacyLab,this.#d(),a$3(this.#e.runtime.three,r));return {scene:e.key,mode:"precompiled",...p,frame:c,firstFrameScreenshot:l,beforeScreenshot:l,screenshot:y,errors:[...i.page,...i.console]}}finally{await A(()=>s.close());}}timingEnvironment(){return this.#y??{adapter:this.#f==="webgl"?"WebGL adapter":"WebGPU adapter",platform:`${x.platform}/${x.arch}`}}async close(){this.#r.rejectAll(new Error("Shader browser driver closed.")),this.#t&&await A(()=>this.#t?.close()??Promise.resolve());let e=this.#s?.httpServer,r=e?Reflect.get(e,"closeAllConnections"):void 0;if(e&&typeof r=="function"&&Reflect.apply(r,e,[]),this.#s&&await A(()=>this.#s?.close()??Promise.resolve()),this.#i){let n=this.#i;n.closeAllConnections(),await A(()=>new Promise(a=>{n.close(()=>a());}));}if(this.#t=void 0,this.#s=void 0,this.#i=void 0,this.#a=void 0,this.#h=void 0,this.#m=void 0,this.#l=void 0,this.#f=void 0,this.#y=void 0,this.#u=0,this.#c!==void 0&&(await rm(this.#c,{recursive:true,force:true}),this.#c=void 0),this.#p){for(let[n,a]of Object.entries(this.#p))a===void 0?delete x.env[n]:x.env[n]=a;this.#p=void 0;}}};function Jr(t){return new H(t)}a(Jr,"createPlaywrightShaderDriver");function qr(t){return new H(t)}a(qr,"createStaticPlaywrightShaderDriver");function zr(t){return new H(t)}a(zr,"createAttachedPlaywrightShaderDriver");export{sr as a,ir as b,le as c,gr as d,_e as e,Jr as f,qr as g,zr as h};
package/dist/cli.js CHANGED
@@ -1,24 +1,30 @@
1
1
  #!/usr/bin/env node
2
- import {c,b as b$1,h,e,a as a$2,f,d}from'./chunk-76WRV5MZ.js';import {a as a$1}from'./chunk-ZT6T2W4B.js';import {a}from'./chunk-HOZDD6PW.js';import o from'process';var r=o.argv.slice(2),ne=r.indexOf("--reporter"),G=r.find(e=>e.startsWith("--reporter="))?.slice(11)??(ne===-1?void 0:r[ne+1]),ae=ne!==-1&&(G===void 0||G.startsWith("--"))?'--reporter requires "plain" or "json".':G!==void 0&&G!=="json"&&G!=="plain"?'--reporter must be "plain" or "json".':void 0,g=r.includes("--json")||G==="json",ce=new Set(["--reporter","--root","--route","--build-mode","--expect-shaders","--width","--height","--timeout","--settle","--min-luminance","--hmr","--size","--output","--driver","--serve","--url","--runs","--debounce","--config","--mode"]),re=[];for(let e=0;e<r.length;e++){let t=r[e];if(t!==void 0){if(t.startsWith("--")){ce.has(t)&&e++;continue}re.push(t);}}var y=re[0]??"status",R=re[1];function $(e){let t=r.find(u=>u.startsWith(`${e}=`));if(t!==void 0){let u=t.slice(e.length+1);if(u.length===0)throw new Error(`${e} requires a value.`);return u}let i=r.indexOf(e);if(i===-1)return;let n=r[i+1];if(n===void 0||n.startsWith("--"))throw new Error(`${e} requires a value.`);return n}a($,"optionValue");function le(e){let t=[];for(let i=0;i<r.length;i++){let n=r[i];if(n!==void 0){if(n.startsWith(`${e}=`)){let u=n.slice(e.length+1);if(u.length===0)throw new Error(`${e} requires a value.`);t.push(u);}else if(n===e){let u=r[i+1];if(u===void 0||u.startsWith("--"))throw new Error(`${e} requires a value.`);t.push(u);}}}return t}a(le,"optionValues");function b(e){let t=$(e);if(t===void 0)return;let i=Number(t);if(!Number.isFinite(i))throw new Error(`${e} must be a finite number.`);return i}a(b,"numberOption");async function pe(e){let t=`Shader provider adapter patch:
2
+ import {c,b,h as h$1,e,a as a$2,f,d}from'./chunk-IILE5RBI.js';import {a as a$1}from'./chunk-ZT6T2W4B.js';import {a}from'./chunk-HOZDD6PW.js';import s from'process';var n=s.argv.slice(2),ae=n.indexOf("--reporter"),H=n.find(e=>e.startsWith("--reporter="))?.slice(11)??(ae===-1?void 0:n[ae+1]),ue=ae!==-1&&(H===void 0||H.startsWith("--"))?'--reporter requires "plain" or "json".':H!==void 0&&H!=="json"&&H!=="plain"?'--reporter must be "plain" or "json".':void 0,h=n.includes("--json")||H==="json",pe=new Set(["--reporter","--root","--route","--build-mode","--expect-shaders","--width","--height","--timeout","--settle","--min-luminance","--hmr","--size","--output","--driver","--serve","--url","--runs","--debounce","--config","--mode","--out","--max-dim","--hdr-max-dim","--quality","--preset"]),se=[];for(let e=0;e<n.length;e++){let t=n[e];if(t!==void 0){if(t.startsWith("--")){pe.has(t)&&e++;continue}se.push(t);}}var v=se[0]??"status",O=se[1];function $(e){let t=n.find(c=>c.startsWith(`${e}=`));if(t!==void 0){let c=t.slice(e.length+1);if(c.length===0)throw new Error(`${e} requires a value.`);return c}let o=n.indexOf(e);if(o===-1)return;let r=n[o+1];if(r===void 0||r.startsWith("--"))throw new Error(`${e} requires a value.`);return r}a($,"optionValue");function fe(e){let t=[];for(let o=0;o<n.length;o++){let r=n[o];if(r!==void 0){if(r.startsWith(`${e}=`)){let c=r.slice(e.length+1);if(c.length===0)throw new Error(`${e} requires a value.`);t.push(c);}else if(r===e){let c=n[o+1];if(c===void 0||c.startsWith("--"))throw new Error(`${e} requires a value.`);t.push(c);}}}return t}a(fe,"optionValues");function k(e){let t=$(e);if(t===void 0)return;let o=Number(t);if(!Number.isFinite(o))throw new Error(`${e} must be a finite number.`);return o}a(k,"numberOption");async function he(e){let t=`Shader provider adapter patch:
3
3
  ${e}
4
- `;if(o.stderr.write(g?`${JSON.stringify({type:"shader-adapter-diff",diff:e})}
5
- `:t),r.includes("--yes"))return true;if(!o.stdin.isTTY||!o.stderr.isTTY)throw new Error("Adapter wiring needs consent. Review the diff above, then rerun with --yes; no files were changed.");let{createInterface:i}=await import('readline/promises'),n=i({input:o.stdin,output:o.stderr});try{let u=await n.question("Apply this patch? (y/N) ");return /^y(?:es)?$/iu.test(u.trim())}finally{n.close();}}a(pe,"approveAdapterPatch");function W(e){return g?{update(){},stop(){}}:h(e)}a(W,"wait");function fe(){let e$1=a(f=>b$1.accent(f),"c"),t=a(f=>b$1.dim(f),"d"),i=c.mark?`${c.mark} `:"",n=[["Inspect",[[`${e$1("status")}`,`project report: shaders, text, browser evidence ${t("\u2014 no browser, no GPU")}`],[`${e$1("shaders")}`,"verify shader freshness and exit 1 with the exact recovery command"],[`${e$1("shaders status")} ${t("[--check]")}`,`per-scene shader freshness ${t("(--check exits 1 when not fresh)")}`],[`${e$1("text status")}`,"text atlas coverage"]]],["Produce",[[`${e$1("shaders capture")} ${t("[--if-stale] [--commit-timing] [--driver static --serve dist]")}`,"capture shaders plus a local timing estimate; during development use the overlay Capture button"],[`${e$1("shaders watch")} ${t("[--debounce <ms>]")}`,"recapture automatically while you work"],[`${e$1("text generate")} ${t("[--config <path>]")}`,"regenerate MSDF text atlases"],[`${e$1("environment bake")} ${t("[--size 256]")}`,"bake the PMREM room environment to KTX2"]]],["Verify",[[`${e$1("shaders test")}`,"median timing + zero-build + compute + pixel parity gate"],[`${e$1("shaders test --parity-only")}`,"one correctness pair per scene; no timing or published-page soak"],[`${e$1("browser smoke")} ${t("[--route /] [--hmr <module>]")}`,"boot the dev app, verify worker lifecycle + HMR"],[`${e$1("browser preview")} ${t("[--route /]...")}`,"build + serve production, verify precompiled hydration"]]]],u=["",`${i}${b$1.bold("three-blocks devtools")} ${b$1.dim(`v${a$1}`)}`,b$1.dim("WebGPU shader precompilation and verification for Three Blocks projects."),"",`${b$1.bold("Usage")}`,` ${e$1("three-blocks-devtools")} ${t("[command] [options] [--json]")}`];for(let[f,a]of n)u.push("",b$1.bold(f),...e(a,{indent:2,maxWidth:a$2.width-2}));u.push("",b$1.bold("Options"),...e([[t("--json"),"stable machine-readable report on stdout"],[t("--reporter <plain|json>"),"select the stable terminal or CI reporter"],[t("--root <path>"),"project root (defaults to the nearest three-blocks project)"],[t("--backend <webgpu|webgl|both>"),"select shader backend lanes (default: config, then both)"],[t("--headed"),"show the automation browser window"],[t("--runs <n>"),"shader timing captures after warmup (default 5)"],[t("--parity-only"),"skip timing warmup/repeats and the published-page soak"],[t("--commit-timing"),"also write shareable hardware-labelled build timing"],[t("--skip-browser"),"check shader schema and freshness without browser parity"],[t("--driver <vite|static>"),"capture through Vite or an already-built static app"],[t("--serve <path>"),"built application directory for --driver static"],[t("--url <url>"),"attach to an already-running transformed application"],[t("--yes"),"apply a previewed framework adapter patch non-interactively"]],{indent:2,maxWidth:a$2.width-2}),"",b$1.dim("During development, use the in-page overlay Capture button for one-click recapture."),b$1.dim("`shaders capture` remains the explicit isolated-browser fallback for batch and CI."),""),f(u.join(`
6
- `));}a(fe,"help");function he(e){return e?`${e.render} render \xB7 ${e.compute} compute`:"no coverage"}a(he,"coverageText");function Z(e){return e<100?e.toFixed(1):Math.round(e).toString()}a(Z,"milliseconds");function q(e,t,i){return `${e} ${d(t,12)} ${i}`}a(q,"row");function de(e$1){if(e$1.skipped)return q(c.tick,"shaders",`fresh ${c.dot} capture skipped ${b$1.dim(`(--if-stale) \xB7 ${e$1.reason}`)}`);let t=Math.max(0,Math.round((e$1.durationMs??0)/1e3)),i=e$1.scenes.length,n=[q(c.tick,"shaders",`captured in ${t} s ${c.dot} wrote ${i} shader scene${i===1?"":"s"}`)];e$1.configuration?.created===true&&n.push(b$1.dim(` created ${e$1.configuration.file} with the default "/" capture route`));for(let f of e$1.scenes)n.push(q(c.tick,f.scene,`${he(f.coverage)} ${c.dot} ${b$1.dim(`${f.attempts} attempt${f.attempts===1?"":"s"}`)}`));let u=e$1.scenes.flatMap(f=>{let a$1=f.timing;if(a$1?.launch===void 0||a$1.firstRequest===void 0||a$1.readiness===void 0||a$1.lastBuild===void 0||a$1.settled===void 0||a$1.posted===void 0)return [];let m=a((O,l)=>`${Z(Math.max(0,l-O))} ms`,"elapsed");return [[f.scene,`launch\u2192request ${m(a$1.launch,a$1.firstRequest)} ${c.dot} ready ${m(a$1.firstRequest,a$1.readiness)} ${c.dot} last build ${m(a$1.readiness,a$1.lastBuild)} ${c.dot} settle ${m(a$1.lastBuild,a$1.settled)} ${c.dot} post ${m(a$1.settled,a$1.posted)}`]]});return u.length>0&&n.push("",b$1.bold("Capture stages"),...e(u,{indent:2,maxWidth:a$2.width-2})),e$1.measurement!==void 0&&n.push(` TSL builds at startup: ${e$1.measurement.beforeBuilds} \u2192 ${e$1.measurement.afterBuilds} ${c.dot} \u2248${Z(Math.abs(e$1.measurement.firstFrameDeltaMs))} ms TSL work avoided (single-capture estimate)`),n.push(b$1.dim(" a running dev server reloads on its own; otherwise refresh the page")),n.join(`
7
- `)}a(de,"formatShaderCapture");function me(e){let t=[q(c.tick,"shaders",`no-GPU ${e.noGpu.state} ${c.dot} browser ${e.browser}`)];for(let i of e.scenes){let n=e.timing?.scenes[i.scene],u=n===void 0?`${i.coverage.render+i.coverage.compute} TSL build${i.coverage.render+i.coverage.compute===1?"":"s"} skipped`:`${n.builds} TSL build${n.builds===1?"":"s"} skipped ${c.dot} \u2248${Z(n.avoidedBuildMs)} ms \xB1 ${Z(n.spreadMs)} TSL work avoided ${c.dot} median of ${n.runs} ${c.dot} Latest local capture`,f=n!==void 0&&n.spreadMs>n.avoidedBuildMs*.4?` ${c.dot} ${b$1.warn("noisy measurement \u2014 close other GPU apps and re-run")}`:"";t.push(q(c.tick,i.scene,`${u} ${c.dot} ${i.coverage.compute} compute ${c.dot} ${(i.parity.diffRatio*100).toFixed(3)}% pixel diff${f}`));}return t.join(`
8
- `)}a(me,"formatShaderTest");async function we(){if(ae!==void 0)throw new Error(ae);if(y==="help"||y==="--help"||r.includes("--help"))return fe();if(y==="version"||r.includes("--version")){f(g?JSON.stringify({version:a$1}):a$1);return}if(y==="browser"){let{formatBrowserPreviewReport:a,formatBrowserSmokeReport:m,runBrowserPreview:O,runBrowserSmoke:l}=await import('./browser-smoke-JHZLPGEB.js'),{findProjectRoot:k}=await import('./status-RFA7WAM6.js');if(R==="preview"){let P=$("--root")??await k(),M=le("--route"),B=$("--build-mode"),T=$("--expect-shaders")??"precompiled";if(T!=="precompiled"&&T!=="live")throw new Error('--expect-shaders must be "precompiled" or "live".');let I=b("--width"),D=b("--height"),j=b("--timeout"),J=b("--settle"),A=b("--min-luminance"),V=W("building and verifying the production bundle"),E;try{E=await O({root:P,...M.length===0?{}:{routes:M},...B===void 0?{}:{buildMode:B},expectShaders:T,text:r.includes("--text"),...j===void 0?{}:{timeoutMs:j},...J===void 0?{}:{settleMs:J},...A===void 0?{}:{minimumLuminance:A},...I===void 0&&D===void 0?{}:{viewport:{width:I??1024,height:D??640}},headed:r.includes("--headed")});}finally{V.stop();}o.stdout.write(`${g?JSON.stringify(E,null,2):a(E)}
9
- `),E.passed||(o.exitCode=1);return}if(R!=="smoke")throw new Error(`Unsupported browser command "${R??""}".`);let S=$("--root")??await k(),h=b("--width"),x=b("--height"),v=$("--route"),N=b("--timeout"),L=b("--settle"),_=b("--min-luminance"),U=$("--hmr");if(r.includes("--webgl")&&r.includes("--unsupported-worker"))throw new Error("--webgl and --unsupported-worker are mutually exclusive.");let K=W(`smoking ${v??"/"} in a real browser`),w;try{w=await l({root:S,...v===void 0?{}:{route:v},...N===void 0?{}:{timeoutMs:N},...L===void 0?{}:{settleMs:L},..._===void 0?{}:{minimumLuminance:_},...U===void 0?{}:{hmrModule:U},forceWebgl:r.includes("--webgl"),unsupportedWorker:r.includes("--unsupported-worker"),expectTextGenerationFailure:r.includes("--expect-text-generation-failure"),...h===void 0&&x===void 0?{}:{viewport:{width:h??1024,height:x??640}},headed:r.includes("--headed")});}finally{K.stop();}o.stdout.write(`${g?JSON.stringify(w,null,2):m(w)}
10
- `),w.passed||(o.exitCode=1);return}if(y==="environment"){if(R!=="bake")throw new Error(`Unsupported environment command "${R??""}".`);let{bakeRoomEnvironment:a,formatEnvironmentBakeReport:m}=await import('./environment-bake-BGMGFAVK.js'),{findProjectRoot:O}=await import('./status-RFA7WAM6.js'),l=$("--root")??await O(),k=b("--size"),S=$("--output"),h=b("--timeout"),x=W("baking the room environment on the GPU"),v;try{v=await a({root:l,...k===void 0?{}:{size:k},...S===void 0?{}:{output:S},...h===void 0?{}:{timeoutMs:h},compression:r.includes("--no-zstd")?"none":"zstd",headed:r.includes("--headed")});}finally{x.stop();}o.stdout.write(`${g?JSON.stringify(v,null,2):m(v)}
11
- `);return}if(y==="shaders"){let{findShaderProjectRoot:a$1}=await import('./shader-config-EOFU5D3Y.js'),m=R??"status";if(!["status","capture","test","watch"].includes(m))throw new Error(`Unsupported shader command "${m}".`);let O=$("--root"),l=await a$1(O),k=$("--driver");if(k!==void 0&&k!=="vite"&&k!=="static")throw new Error('--driver must be "vite" or "static".');let S=k,h=$("--serve"),x=$("--url"),v=$("--backend");if(v!==void 0&&v!=="webgpu"&&v!=="webgl"&&v!=="both")throw new Error('--backend must be "webgpu", "webgl", or "both".');let N=v;if(m==="status"){let{inspectShaderMatrix:w}=await import('./shader-workflow-T7W7UHWJ.js'),{formatShaderMatrixStatus:P}=await import('./status-RFA7WAM6.js'),M=await w(l,void 0,N);o.stdout.write(`${g?JSON.stringify(M,null,2):P(M)}
12
- `),(r.includes("--check")||R===void 0)&&M.state!=="fresh"&&(o.exitCode=1);return}if(m==="capture"){let{captureShaders:w}=await import('./shader-workflow-T7W7UHWJ.js'),{applyShaderAutoWire:P,hasRegisterDevtoolsCall:M,inspectShaderAutoWire:B}=await import('./shader-autowire-SPE5DM2X.js'),{buildShaderCaptureProject:T,startNextShaderCaptureServer:I}=await import('./shader-project-runner-S2AID2RJ.js'),{inferShaderCaptureTarget:D}=await import('./shader-config-EOFU5D3Y.js'),j=S,J=h,A=x,V,E,ee;try{if(j===void 0&&J===void 0&&A===void 0){let c=await B(l);if(!c.configured&&c.plan!==void 0){if(!await pe(c.plan.diff))throw new Error("Adapter wiring was declined; no files were changed.");await P(c.plan);}else if(!c.configured&&c.bundler!=="vite"&&c.bundler!=="unknown")throw new Error(`${c.reason} No files were changed.`);if(c.bundler==="next")E=W("starting the project-local Next.js capture server"),V=await I(l),A=V.url,E.update("capturing shader scenes");else if(c.bundler==="webpack"||c.bundler==="esbuild"||c.bundler==="rollup"){E=W(`building the ${c.bundler} capture target`),await T(l);let C=await D(l);if(C.driverKind!=="static"||C.serve===void 0)throw new Error(`${c.bundler} build completed but no dist/, build/, or out/index.html was found; pass --driver static --serve <directory>.`);j="static",J=C.serve,E.update("capturing shader scenes");}}E??=W("capturing shader scenes"),ee=await w({root:l,headed:r.includes("--headed"),ifStale:r.includes("--if-stale"),commitTiming:r.includes("--commit-timing"),...N===void 0?{}:{backend:N},...j===void 0?{}:{driverKind:j},...J===void 0?{}:{serve:J},...A===void 0?{}:{url:A},onSceneStart:a((c,C,H)=>E?.update(`capturing ${b$1.accent(c)} ${b$1.dim(`(${C+1}/${H})`)}`),"onSceneStart"),onSceneRetry:a((c,C,H,ie,z)=>{let ue=z instanceof Error&&/timed out/iu.test(z.message),se=`capture attempt ${C}/${H} for "${c}" after ${ue?`timeout (${Math.round(ie/1e3)} s burned)`:`failure (${z instanceof Error?z.message:String(z)})`}`;o.stderr.write(g?`${JSON.stringify({type:"shader-capture-retry",scene:c,attempt:C,total:H,timeoutMs:ie,message:se})}
13
- `:`${se}
14
- `);},"onSceneRetry")});}finally{E?.stop(),await V?.close();}if(o.stdout.write(`${g?JSON.stringify(ee,null,2):de(ee)}
15
- `),!await M(l)){let c$1="Add registerDevtools({ renderer }) from three-blocks/devtools so committed shader artifacts hydrate in development and production.";o.stderr.write(g?`${JSON.stringify({type:"shader-runtime-registration",state:"missing",message:c$1})}
16
- `:`${c.warn} ${c$1}
17
- `);}return}if(m==="test"){let w=$("--mode");if(w!==void 0&&w!=="strict")throw new Error('--mode currently supports only "strict".');let{testShaders:P}=await import('./shader-workflow-T7W7UHWJ.js'),M=W("verifying precompiled shaders against the live baseline"),B=b("--runs"),T;try{T=await P({root:l,headed:r.includes("--headed"),skipBrowser:r.includes("--skip-browser"),...S===void 0?{}:{driverKind:S},...h===void 0?{}:{serve:h},...x===void 0?{}:{url:x},...B===void 0?{}:{runs:B},parityOnly:r.includes("--parity-only"),commitTiming:r.includes("--commit-timing"),...N===void 0?{}:{backend:N},onSceneStart:a((I,D,j)=>M.update(`verifying ${b$1.accent(I)} ${b$1.dim(`(${D+1}/${j})`)}`),"onSceneStart")});}finally{M.stop();}o.stdout.write(`${g?JSON.stringify(T,null,2):me(T)}
18
- `);return}let L=b("--debounce"),{watchShaders:_}=await import('./shader-watch-XY6A2KGQ.js'),U=new AbortController;o.once("SIGINT",()=>U.abort()),o.once("SIGTERM",()=>U.abort());let K=await _({root:l,headed:r.includes("--headed"),...S===void 0?{}:{driverKind:S},...h===void 0?{}:{serve:h},...x===void 0?{}:{url:x},...L===void 0?{}:{debounceMs:L},signal:U.signal,onResult(w){o.stdout.write(`${g?JSON.stringify(w):de(w)}
19
- `);},onError(w){o.stderr.write(`${g?JSON.stringify({error:w.message}):`${c.cross} ${b$1.danger(w.message)}`}
20
- `);}});g||f(q(c.ring,"watching",b$1.dim(K.root))),await K.done;return}if(y==="text"&&R&&R!=="status"){if(R!=="generate")throw new Error(`Unsupported text command "${R}".`);let{generateTextAtlases:a}=await import('./text-atlas-WRYJT3E7.js'),{findProjectRoot:m}=await import('./status-RFA7WAM6.js'),O=r.indexOf("--config"),l=O===-1?"three-blocks.text.ts":r[O+1];if(l===void 0||l.startsWith("--"))throw new Error("--config requires a project-relative path.");let k=await m(),S=W("generating MSDF text atlases"),h;try{h=await a(k,l);}finally{S.stop();}if(g)o.stdout.write(`${JSON.stringify(h,null,2)}
21
- `);else {f(q(c.tick,"text",`${h.glyphs} glyphs ${c.dot} ${h.atlases.length} atlas${h.atlases.length===1?"":"es"} regenerated`));for(let x of h.updatedFiles)f(` ${d(b$1.dim("updated"),12)} ${b$1.dim(x)}`);}return}if(!["status","text"].includes(y))throw new Error(`Unknown command "${y}".`);let{formatThreeBlocksStatus:e,inspectThreeBlocksStatus:t,threeBlocksStatusIsFresh:i}=await import('./status-RFA7WAM6.js'),n=await t(),u=(n.text);if(y==="text"&&!u)throw new Error("This project does not enable the text feature.");let f$1=y==="text"?u:n;o.stdout.write(`${g?JSON.stringify(f$1,null,2):y==="status"?e(n):JSON.stringify(f$1,null,2)}
22
- `),y==="status"&&r.includes("--check")&&!i(n)&&(o.exitCode=1);}a(we,"main");we().catch(e=>{let t=e instanceof Error?e.message:String(e);g?o.stderr.write(`${JSON.stringify({error:t})}
23
- `):o.stderr.write(`${c.cross} ${b$1.danger(t)}
24
- `),o.exitCode=1;});
4
+ `;if(s.stderr.write(h?`${JSON.stringify({type:"shader-adapter-diff",diff:e})}
5
+ `:t),n.includes("--yes"))return true;if(!s.stdin.isTTY||!s.stderr.isTTY)throw new Error("Adapter wiring needs consent. Review the diff above, then rerun with --yes; no files were changed.");let{createInterface:o}=await import('readline/promises'),r=o({input:s.stdin,output:s.stderr});try{let c=await r.question("Apply this patch? (y/N) ");return /^y(?:es)?$/iu.test(c.trim())}finally{r.close();}}a(he,"approveAdapterPatch");function C(e){return h?{update(){},stop(){}}:h$1(e)}a(C,"wait");function me(){let e$1=a(b$1=>b.accent(b$1),"c"),t=a(b$1=>b.dim(b$1),"d"),o=c.mark?`${c.mark} `:"",r=[["Inspect",[[`${e$1("status")}`,`project report: shaders, text, browser evidence ${t("\u2014 no browser, no GPU")}`],[`${e$1("shaders")}`,"verify shader freshness and exit 1 with the exact recovery command"],[`${e$1("shaders status")} ${t("[--check]")}`,`per-scene shader freshness ${t("(--check exits 1 when not fresh)")}`],[`${e$1("text status")}`,"text atlas coverage"],[`${e$1("assets status")}`,"optimized-asset receipt freshness"]]],["Produce",[[`${e$1("shaders capture")} ${t("[--if-stale] [--commit-timing] [--no-assets]")}`,"capture shaders plus a local timing estimate, then optimize public GLB/.hdr assets; during development use the overlay Capture button"],[`${e$1("shaders watch")} ${t("[--debounce <ms>]")}`,"recapture automatically while you work"],[`${e$1("text generate")} ${t("[--config <path>]")}`,"regenerate MSDF text atlases"],[`${e$1("environment bake")} ${t("[--size 256]")}`,"bake the PMREM room environment to KTX2"],[`${e$1("assets optimize")} ${t("<file|dir\u2026> [--out <dir>] [--quality high]")}`,"meshopt + slot-aware KTX2 for GLB / image / .hdr inputs"]]],["Verify",[[`${e$1("shaders test")}`,"median timing + zero-build + compute + pixel parity gate"],[`${e$1("shaders test --parity-only")}`,"one correctness pair per scene; no timing or published-page soak"],[`${e$1("browser smoke")} ${t("[--route /] [--hmr <module>]")}`,"boot the dev app, verify worker lifecycle + HMR"],[`${e$1("browser preview")} ${t("[--route /]...")}`,"build + serve production, verify precompiled hydration"]]]],c$1=["",`${o}${b.bold("three-blocks devtools")} ${b.dim(`v${a$1}`)}`,b.dim("WebGPU shader precompilation and verification for Three Blocks projects."),"",`${b.bold("Usage")}`,` ${e$1("three-blocks-devtools")} ${t("[command] [options] [--json]")}`];for(let[b$1,a]of r)c$1.push("",b.bold(b$1),...e(a,{indent:2,maxWidth:a$2.width-2}));c$1.push("",b.bold("Options"),...e([[t("--json"),"stable machine-readable report on stdout"],[t("--reporter <plain|json>"),"select the stable terminal or CI reporter"],[t("--root <path>"),"project root (defaults to the nearest three-blocks project)"],[t("--backend <webgpu|webgl|both>"),"select shader backend lanes (default: config, then both)"],[t("--headed"),"show the automation browser window"],[t("--runs <n>"),"shader timing captures after warmup (default 5)"],[t("--parity-only"),"skip timing warmup/repeats and the published-page soak"],[t("--commit-timing"),"also write shareable hardware-labelled build timing"],[t("--skip-browser"),"check shader schema and freshness without browser parity"],[t("--driver <vite|static>"),"capture through Vite or an already-built static app"],[t("--serve <path>"),"built application directory for --driver static"],[t("--url <url>"),"attach to an already-running transformed application"],[t("--yes"),"apply a previewed framework adapter patch non-interactively"],[t("--out <dir>"),"assets optimize: output directory (default: beside each input)"],[t("--max-dim <px>"),"assets optimize: LDR texture cap (default 2048)"],[t("--quality <default|high>"),"assets optimize: high = UASTC color + higher encoder effort"],[t("--preset <color|normal|data>"),"assets optimize: override the standalone-image preset"],[t("--force"),"assets optimize: re-encode even when cached"]],{indent:2,maxWidth:a$2.width-2}),"",b.dim("During development, use the in-page overlay Capture button for one-click recapture."),b.dim("`shaders capture` remains the explicit isolated-browser fallback for batch and CI."),""),f(c$1.join(`
6
+ `));}a(me,"help");function we(e){return e?`${e.render} render \xB7 ${e.compute} compute`:"no coverage"}a(we,"coverageText");function ie(e){return e<100?e.toFixed(1):Math.round(e).toString()}a(ie,"milliseconds");function L(e,t,o){return `${e} ${d(t,12)} ${o}`}a(L,"row");function ce(e$1){if(e$1.skipped)return L(c.tick,"shaders",`fresh ${c.dot} capture skipped ${b.dim(`(--if-stale) \xB7 ${e$1.reason}`)}`);let t=Math.max(0,Math.round((e$1.durationMs??0)/1e3)),o=e$1.scenes.length,r=[L(c.tick,"shaders",`captured in ${t} s ${c.dot} wrote ${o} shader scene${o===1?"":"s"}`)];e$1.configuration?.created===true&&r.push(b.dim(` created ${e$1.configuration.file} with the default "/" capture route`));for(let b$1 of e$1.scenes)r.push(L(c.tick,b$1.scene,`${we(b$1.coverage)} ${c.dot} ${b.dim(`${b$1.attempts} attempt${b$1.attempts===1?"":"s"}`)}`));let c$1=e$1.scenes.flatMap(b=>{let a$1=b.timing;if(a$1?.launch===void 0||a$1.firstRequest===void 0||a$1.readiness===void 0||a$1.lastBuild===void 0||a$1.settled===void 0||a$1.posted===void 0)return [];let m=a((R,l)=>`${ie(Math.max(0,l-R))} ms`,"elapsed");return [[b.scene,`launch\u2192request ${m(a$1.launch,a$1.firstRequest)} ${c.dot} ready ${m(a$1.firstRequest,a$1.readiness)} ${c.dot} last build ${m(a$1.readiness,a$1.lastBuild)} ${c.dot} settle ${m(a$1.lastBuild,a$1.settled)} ${c.dot} post ${m(a$1.settled,a$1.posted)}`]]});return c$1.length>0&&r.push("",b.bold("Capture stages"),...e(c$1,{indent:2,maxWidth:a$2.width-2})),e$1.measurement!==void 0&&r.push(` TSL builds at startup: ${e$1.measurement.beforeBuilds} \u2192 ${e$1.measurement.afterBuilds} ${c.dot} \u2248${ie(Math.abs(e$1.measurement.firstFrameDeltaMs))} ms TSL work avoided (single-capture estimate)`),r.push(b.dim(" a running dev server reloads on its own; otherwise refresh the page")),r.join(`
7
+ `)}a(ce,"formatShaderCapture");function ge(e){let t=[L(c.tick,"shaders",`no-GPU ${e.noGpu.state} ${c.dot} browser ${e.browser}`)];for(let o of e.scenes){let r=e.timing?.scenes[o.scene],c$1=r===void 0?`${o.coverage.render+o.coverage.compute} TSL build${o.coverage.render+o.coverage.compute===1?"":"s"} skipped`:`${r.builds} TSL build${r.builds===1?"":"s"} skipped ${c.dot} \u2248${ie(r.avoidedBuildMs)} ms \xB1 ${ie(r.spreadMs)} TSL work avoided ${c.dot} median of ${r.runs} ${c.dot} Latest local capture`,b$1=r!==void 0&&r.spreadMs>r.avoidedBuildMs*.4?` ${c.dot} ${b.warn("noisy measurement \u2014 close other GPU apps and re-run")}`:"";t.push(L(c.tick,o.scene,`${c$1} ${c.dot} ${o.coverage.compute} compute ${c.dot} ${(o.parity.diffRatio*100).toFixed(3)}% pixel diff${b$1}`));}return t.join(`
8
+ `)}a(ge,"formatShaderTest");async function $e(){if(ue!==void 0)throw new Error(ue);if(v==="help"||v==="--help"||n.includes("--help"))return me();if(v==="version"||n.includes("--version")){f(h?JSON.stringify({version:a$1}):a$1);return}if(v==="browser"){let{formatBrowserPreviewReport:a,formatBrowserSmokeReport:m,runBrowserPreview:R,runBrowserSmoke:l}=await import('./browser-smoke-WWMWRM6S.js'),{findProjectRoot:y}=await import('./status-EK3BIJ2K.js');if(O==="preview"){let T=$("--root")??await y(),M=fe("--route"),F=$("--build-mode"),A=$("--expect-shaders")??"precompiled";if(A!=="precompiled"&&A!=="live")throw new Error('--expect-shaders must be "precompiled" or "live".');let _=k("--width"),X=k("--height"),P=k("--timeout"),V=k("--settle"),K=k("--min-luminance"),Y=C("building and verifying the production bundle"),z;try{z=await R({root:T,...M.length===0?{}:{routes:M},...F===void 0?{}:{buildMode:F},expectShaders:A,text:n.includes("--text"),...P===void 0?{}:{timeoutMs:P},...V===void 0?{}:{settleMs:V},...K===void 0?{}:{minimumLuminance:K},..._===void 0&&X===void 0?{}:{viewport:{width:_??1024,height:X??640}},headed:n.includes("--headed")});}finally{Y.stop();}s.stdout.write(`${h?JSON.stringify(z,null,2):a(z)}
9
+ `),z.passed||(s.exitCode=1);return}if(O!=="smoke")throw new Error(`Unsupported browser command "${O??""}".`);let w=$("--root")??await y(),p=k("--width"),E=k("--height"),S=$("--route"),N=k("--timeout"),G=k("--settle"),I=k("--min-luminance"),j=$("--hmr");if(n.includes("--webgl")&&n.includes("--unsupported-worker"))throw new Error("--webgl and --unsupported-worker are mutually exclusive.");let B=C(`smoking ${S??"/"} in a real browser`),g;try{g=await l({root:w,...S===void 0?{}:{route:S},...N===void 0?{}:{timeoutMs:N},...G===void 0?{}:{settleMs:G},...I===void 0?{}:{minimumLuminance:I},...j===void 0?{}:{hmrModule:j},forceWebgl:n.includes("--webgl"),unsupportedWorker:n.includes("--unsupported-worker"),expectTextGenerationFailure:n.includes("--expect-text-generation-failure"),...p===void 0&&E===void 0?{}:{viewport:{width:p??1024,height:E??640}},headed:n.includes("--headed")});}finally{B.stop();}s.stdout.write(`${h?JSON.stringify(g,null,2):m(g)}
10
+ `),g.passed||(s.exitCode=1);return}if(v==="assets"||v==="optimize"){let a$1=v==="optimize"?"optimize":O??"status",m=se.slice(v==="optimize"?1:2),{findProjectRoot:R}=await import('./status-EK3BIJ2K.js'),l=$("--root")??await R().catch(()=>s.cwd());if(a$1==="status"){let{formatAssetStatusReport:B,inspectAssets:g}=await import('./asset-optimize-WFEMLDJF.js'),T=await g(l);s.stdout.write(`${h?JSON.stringify(T,null,2):B(T)}
11
+ `),n.includes("--check")&&T.state==="stale"&&(s.exitCode=1);return}if(a$1!=="optimize")throw new Error(`Unsupported assets command "${a$1}".`);if(m.length===0)throw new Error("assets optimize needs at least one file or directory (.glb, .png, .jpg, .webp, .hdr).");let y=$("--quality");if(y!==void 0&&y!=="default"&&y!=="high")throw new Error('--quality must be "default" or "high".');let w=$("--preset");if(w!==void 0&&w!=="color"&&w!=="normal"&&w!=="data")throw new Error('--preset must be "color", "normal", or "data".');let p=$("--out"),E=k("--max-dim"),S=k("--hdr-max-dim"),{formatAssetOptimizeReport:N,optimizeAssets:G}=await import('./asset-optimize-WFEMLDJF.js'),I=C("optimizing assets"),j;try{j=await G({root:l,inputs:m,...p===void 0?{}:{outDir:p},...E===void 0?{}:{maxDimension:E},...S===void 0?{}:{hdrMaxDimension:S},...y===void 0?{}:{quality:y},...w===void 0?{}:{preset:w},force:n.includes("--force"),onProgress:a(B=>I.update(`optimizing ${b.accent(B)}`),"onProgress")});}finally{I.stop();}s.stdout.write(`${h?JSON.stringify(j,null,2):N(j)}
12
+ `),j.failed.length>0&&(s.exitCode=1);return}if(v==="environment"){if(O!=="bake")throw new Error(`Unsupported environment command "${O??""}".`);let{bakeRoomEnvironment:a,formatEnvironmentBakeReport:m}=await import('./environment-bake-BGMGFAVK.js'),{findProjectRoot:R}=await import('./status-EK3BIJ2K.js'),l=$("--root")??await R(),y=k("--size"),w=$("--output"),p=k("--timeout"),E=C("baking the room environment on the GPU"),S;try{S=await a({root:l,...y===void 0?{}:{size:y},...w===void 0?{}:{output:w},...p===void 0?{}:{timeoutMs:p},compression:n.includes("--no-zstd")?"none":"zstd",headed:n.includes("--headed")});}finally{E.stop();}s.stdout.write(`${h?JSON.stringify(S,null,2):m(S)}
13
+ `);return}if(v==="shaders"){let{findShaderProjectRoot:a$1}=await import('./shader-config-EOFU5D3Y.js'),m=O??"status";if(!["status","capture","test","watch"].includes(m))throw new Error(`Unsupported shader command "${m}".`);let R=$("--root"),l=await a$1(R),y=$("--driver");if(y!==void 0&&y!=="vite"&&y!=="static")throw new Error('--driver must be "vite" or "static".');let w=y,p=$("--serve"),E=$("--url"),S=$("--backend");if(S!==void 0&&S!=="webgpu"&&S!=="webgl"&&S!=="both")throw new Error('--backend must be "webgpu", "webgl", or "both".');let N=S;if(m==="status"){let{inspectShaderMatrix:g}=await import('./shader-workflow-BYF3IJOU.js'),{formatShaderMatrixStatus:T}=await import('./status-EK3BIJ2K.js'),M=await g(l,void 0,N);s.stdout.write(`${h?JSON.stringify(M,null,2):T(M)}
14
+ `),(n.includes("--check")||O===void 0)&&M.state!=="fresh"&&(s.exitCode=1);return}if(m==="capture"){let{captureShaders:g}=await import('./shader-workflow-BYF3IJOU.js'),{applyShaderAutoWire:T,hasRegisterDevtoolsCall:M,inspectShaderAutoWire:F}=await import('./shader-autowire-SPE5DM2X.js'),{buildShaderCaptureProject:A,startNextShaderCaptureServer:_}=await import('./shader-project-runner-S2AID2RJ.js'),{inferShaderCaptureTarget:X}=await import('./shader-config-EOFU5D3Y.js'),P=w,V=p,K=E,Y,z,Q;try{if(P===void 0&&V===void 0&&K===void 0){let d=await F(l);if(!d.configured&&d.plan!==void 0){if(!await he(d.plan.diff))throw new Error("Adapter wiring was declined; no files were changed.");await T(d.plan);}else if(!d.configured&&d.bundler!=="vite"&&d.bundler!=="unknown")throw new Error(`${d.reason} No files were changed.`);if(d.bundler==="next")z=C("starting the project-local Next.js capture server"),Y=await _(l),K=Y.url,z.update("capturing shader scenes");else if(d.bundler==="webpack"||d.bundler==="esbuild"||d.bundler==="rollup"){z=C(`building the ${d.bundler} capture target`),await A(l);let x=await X(l);if(x.driverKind!=="static"||x.serve===void 0)throw new Error(`${d.bundler} build completed but no dist/, build/, or out/index.html was found; pass --driver static --serve <directory>.`);P="static",V=x.serve,z.update("capturing shader scenes");}}z??=C("capturing shader scenes"),Q=await g({root:l,headed:n.includes("--headed"),ifStale:n.includes("--if-stale"),commitTiming:n.includes("--commit-timing"),...N===void 0?{}:{backend:N},...P===void 0?{}:{driverKind:P},...V===void 0?{}:{serve:V},...K===void 0?{}:{url:K},onSceneStart:a((d,x,W)=>z?.update(`capturing ${b.accent(d)} ${b.dim(`(${x+1}/${W})`)}`),"onSceneStart"),onSceneRetry:a((d,x,W,q,D)=>{let Z=D instanceof Error&&/timed out/iu.test(D.message),ee=`capture attempt ${x}/${W} for "${d}" after ${Z?`timeout (${Math.round(q/1e3)} s burned)`:`failure (${D instanceof Error?D.message:String(D)})`}`;s.stderr.write(h?`${JSON.stringify({type:"shader-capture-retry",scene:d,attempt:x,total:W,timeoutMs:q,message:ee})}
15
+ `:`${ee}
16
+ `);},"onSceneRetry")});}finally{z?.stop(),await Y?.close();}let J;if(!n.includes("--no-assets"))try{let{discoverPublicAssets:d,optimizeAssets:x}=await import('./asset-optimize-WFEMLDJF.js'),{loadShaderToolConfig:W}=await import('./shader-config-EOFU5D3Y.js'),q=(await W(l)).publicDir??"public",D=await d(l,q);if(D.length>0){let Z=C("optimizing project assets");try{J=await x({root:l,inputs:D,onProgress:a(ee=>Z.update(`optimizing ${b.accent(ee)}`),"onProgress")});}finally{Z.stop();}}}catch(d){let x=`Asset optimization skipped: ${d instanceof Error?d.message:String(d)}`;s.stderr.write(h?`${JSON.stringify({type:"asset-optimize-error",message:x})}
17
+ `:`${c.warn} ${x}
18
+ `);}let le=J===void 0?Q:{...Q,assets:J};if(s.stdout.write(`${h?JSON.stringify(le,null,2):ce(Q)}
19
+ `),!h&&J!==void 0){let{formatAssetOptimizeReport:d}=await import('./asset-optimize-WFEMLDJF.js'),x=J.results.filter(q=>q.cached).length;f(L(J.failed.length>0?c.warn:c.tick,"assets",`${J.results.length} optimized${x>0?` (${x} cached)`:""} ${c.dot} ${b.dim("meshopt + KTX2, receipt .three-blocks/assets/meta.json")}`));let W=d(J);W.length>0&&f(W.split(`
20
+ `).map(q=>` ${q}`).join(`
21
+ `));}if(!await M(l)){let d="Add registerDevtools({ renderer }) from three-blocks/devtools so committed shader artifacts hydrate in development and production.";s.stderr.write(h?`${JSON.stringify({type:"shader-runtime-registration",state:"missing",message:d})}
22
+ `:`${c.warn} ${d}
23
+ `);}return}if(m==="test"){let g=$("--mode");if(g!==void 0&&g!=="strict")throw new Error('--mode currently supports only "strict".');let{testShaders:T}=await import('./shader-workflow-BYF3IJOU.js'),M=C("verifying precompiled shaders against the live baseline"),F=k("--runs"),A;try{A=await T({root:l,headed:n.includes("--headed"),skipBrowser:n.includes("--skip-browser"),...w===void 0?{}:{driverKind:w},...p===void 0?{}:{serve:p},...E===void 0?{}:{url:E},...F===void 0?{}:{runs:F},parityOnly:n.includes("--parity-only"),commitTiming:n.includes("--commit-timing"),...N===void 0?{}:{backend:N},onSceneStart:a((_,X,P)=>M.update(`verifying ${b.accent(_)} ${b.dim(`(${X+1}/${P})`)}`),"onSceneStart")});}finally{M.stop();}s.stdout.write(`${h?JSON.stringify(A,null,2):ge(A)}
24
+ `);return}let G=k("--debounce"),{watchShaders:I}=await import('./shader-watch-TLPGG53E.js'),j=new AbortController;s.once("SIGINT",()=>j.abort()),s.once("SIGTERM",()=>j.abort());let B=await I({root:l,headed:n.includes("--headed"),...w===void 0?{}:{driverKind:w},...p===void 0?{}:{serve:p},...E===void 0?{}:{url:E},...G===void 0?{}:{debounceMs:G},signal:j.signal,onResult(g){s.stdout.write(`${h?JSON.stringify(g):ce(g)}
25
+ `);},onError(g){s.stderr.write(`${h?JSON.stringify({error:g.message}):`${c.cross} ${b.danger(g.message)}`}
26
+ `);}});h||f(L(c.ring,"watching",b.dim(B.root))),await B.done;return}if(v==="text"&&O&&O!=="status"){if(O!=="generate")throw new Error(`Unsupported text command "${O}".`);let{generateTextAtlases:a}=await import('./text-atlas-WRYJT3E7.js'),{findProjectRoot:m}=await import('./status-EK3BIJ2K.js'),R=n.indexOf("--config"),l=R===-1?"three-blocks.text.ts":n[R+1];if(l===void 0||l.startsWith("--"))throw new Error("--config requires a project-relative path.");let y=await m(),w=C("generating MSDF text atlases"),p;try{p=await a(y,l);}finally{w.stop();}if(h)s.stdout.write(`${JSON.stringify(p,null,2)}
27
+ `);else {f(L(c.tick,"text",`${p.glyphs} glyphs ${c.dot} ${p.atlases.length} atlas${p.atlases.length===1?"":"es"} regenerated`));for(let E of p.updatedFiles)f(` ${d(b.dim("updated"),12)} ${b.dim(E)}`);}return}if(!["status","text"].includes(v))throw new Error(`Unknown command "${v}".`);let{formatThreeBlocksStatus:e,inspectThreeBlocksStatus:t,threeBlocksStatusIsFresh:o}=await import('./status-EK3BIJ2K.js'),r=await t(),c$1=(r.text);if(v==="text"&&!c$1)throw new Error("This project does not enable the text feature.");let b$1=v==="text"?c$1:r;s.stdout.write(`${h?JSON.stringify(b$1,null,2):v==="status"?e(r):JSON.stringify(b$1,null,2)}
28
+ `),v==="status"&&n.includes("--check")&&!o(r)&&(s.exitCode=1);}a($e,"main");$e().catch(e=>{let t=e instanceof Error?e.message:String(e);h?s.stderr.write(`${JSON.stringify({error:t})}
29
+ `):s.stderr.write(`${c.cross} ${b.danger(t)}
30
+ `),s.exitCode=1;});
package/dist/index.d.ts CHANGED
@@ -1236,4 +1236,110 @@ interface ShaderWatchSession {
1236
1236
  /** Opt-in filesystem watch. A failed recapture leaves the prior receipt in place and watching continues. */
1237
1237
  declare function watchShaders(options?: WatchShadersOptions): Promise<ShaderWatchSession>;
1238
1238
 
1239
- export { type AdoptedProjectStatus, type ArtifactState, type AttachedPlaywrightShaderDriverOptions, type BrowserPreviewOptions, type BrowserPreviewReport, type BrowserPreviewRouteReport, type BrowserSmokeBuildReceipt, type BrowserSmokeCodec, type BrowserSmokeCodecRoute, type BrowserSmokeHmr, type BrowserSmokeHmrEvidence, type BrowserSmokeLifecycle, type BrowserSmokeOptions, type BrowserSmokeReport, type BrowserSmokeWorkerRestart, type BuiltinFontMetadata, type CaptureSceneResult, type CaptureShadersOptions, type CaptureShadersResult, DOCUMENTED_BROWSER_NOISE, type DiscoveredTextContent, type EncodedRoomEnvironment, type EnsuredShaderToolConfig, type EnvironmentBakeOptions, type EnvironmentBakeReport, type GenerateTextResult, type GeneratedTextAtlas, type GlyphSet, type GlyphSetOptions, type InferredShaderCaptureTarget, type InstalledShaderRuntime, type PlaywrightShaderDriverOptions, type ProjectMetadataV2, type ProjectStatus, RUNTIME_STATUS_RECEIPT_MAX_BYTES, RUNTIME_STATUS_RECEIPT_PATH, RUNTIME_STATUS_RECOVERY, type RoomEnvironmentCapture, type RoomEnvironmentCaptureOptions, type RoomEnvironmentCaptureResult, type RoomEnvironmentMetadata, type RuntimeAssetsStatus, type RuntimeBackend, type RuntimeCodecObservation, type RuntimeHmrCapabilities, type RuntimeHmrOutcome, type RuntimeHmrStatus, type RuntimeObservation, type RuntimeOptimizationStatus, type RuntimeState, type RuntimeStatsStatus, type RuntimeStatusReceiptV1, type RuntimeTextStatus, type RuntimeWorkerStatus, SHADER_ARTIFACT_DIRECTORY, SHADER_COMMITTED_TIMING_FILE, SHADER_DEVTOOLS_VERSION, SHADER_LOCAL_TIMING_FILE, SHADER_PARITY_DIRECTORY, SHADER_PARITY_EVIDENCE_FILE, SHADER_PROVIDER_TRANSFORM_VERSION, SHADER_TOOL_CONFIG_FILE, type ShaderBridgeConfig, type ShaderBrowserDriver, type ShaderBrowserObservation, type ShaderBrowserReadiness, type ShaderCapturePayload, type ShaderCaptureProvenance, type ShaderCaptureStageTiming, type ShaderCaptureStats, type ShaderCommittedTiming, type ShaderCoverage, type ShaderFreshness, type ShaderLocalTiming, type ShaderMatrixState, type ShaderMatrixStatus, type ShaderParityConfig, type ShaderParityEvidence, type ShaderParityResult, type ShaderReadinessConfig, type ShaderReceiptV2, type ShaderSceneArtifactStatus, type ShaderSceneConfig, type ShaderSceneParityEvidence, type ShaderSceneReceipt, type ShaderSceneTestResult, type ShaderSceneTiming, type ShaderScreenshotMetrics, type ShaderSkippedBuild, type ShaderStartupMeasurement, type ShaderState, type ShaderStatus, type ShaderTiming, type ShaderTimingEnvironment, type ShaderToolConfig, ShaderWatchQueue, type ShaderWatchQueueOptions, type ShaderWatchSession, type StaticPlaywrightShaderDriverOptions, TEXT_ATLAS_GENERATOR_VERSION, TEXT_ATLAS_SCHEMA_VERSION, type TestShadersOptions, type TestShadersResult, type TextAtlasFileReceipt, type TextAtlasGenerationReceipt, type TextAtlasInspection, type TextAtlasRuntimeOptions, type TextAtlasStatus, type TextState, type TextStatus, type TextUnicodePreset, type TextUnicodeRange, type ThreeBlocksStatus, type ToolTextConfiguration, type ToolTextFontRoute, type ToolTextFontSource, type ToolTextGenerationOptions, type ValidateShaderCaptureOptions, type ValidatedShaderCapture, type WatchShadersOptions, type WriteShaderArtifactsOptions, type WrittenShaderArtifacts, bakeRoomEnvironment, buildGlyphSet, builtinFontMetadata, captureRoomEnvironment, captureShaders, compareShaderScreenshots, createAttachedPlaywrightShaderDriver, createPlaywrightShaderDriver, createStaticPlaywrightShaderDriver, discoverTextContent, encodeRoomEnvironmentKtx2, ensureShaderToolConfig, extractCanvasTextFromHtml, extractDefinedTextContent, findProjectRoot, findShaderProjectRoot, formatBrowserPreviewReport, formatBrowserSmokeReport, formatEnvironmentBakeReport, formatShaderMatrixStatus, formatStatus, formatThreeBlocksStatus, generateTextAtlases, inferShaderCaptureTarget, inspectProject, inspectShaderMatrix, inspectTextAtlases, inspectThreeBlocksStatus, isDocumentedBrowserNoise, isRetryableShaderObservationNavigation, loadShaderCaptureRuntimeSource, loadShaderToolConfig, parseBrowserSmokeLifecycle, parseProjectMetadata, parseRuntimeStatusReceipt, parseShaderReceipt, parseShaderToolConfig, parseTextAtlasReceipt, parseTextConfiguration, prewarmBuiltinFont, readInstalledShaderRuntime, readShaderCommittedTiming, readShaderLocalTiming, readShaderParityEvidence, readShaderTiming, readTextConfiguration, runBrowserPreview, runBrowserSmoke, screenshotLuminance, serializeShaderManifest, shaderCoverage, shaderScreenshotMetrics, snapshotShaderInputs, stableStringify, testShaders, threeBlocksStatusIsFresh, validateEnvironmentCubeSize, validateShaderCapture, watchShaders, webgpuBrowserArgs, webgpuBrowserHeadless, writeShaderArtifacts, writeShaderLocalTiming, writeShaderParityEvidence, writeShaderTimingTwins };
1239
+ /**
1240
+ * Asset optimization: meshopt geometry + slot-aware Basis Universal (KTX2)
1241
+ * textures for GLB/image/HDR inputs, with a content-hash encode cache and a
1242
+ * receipt committed last. The heavy encoders (sharp, gltf-transform,
1243
+ * meshoptimizer, ktx2-encoder) load lazily so importing this module stays
1244
+ * cheap for the Vite plugin's self-inspection path.
1245
+ *
1246
+ * Verified contract (docs/plans/asset-optimize.md §5): `.hdr` inputs must be
1247
+ * Y-flipped at encode, data/normal slots must not carry the sRGB transfer in
1248
+ * their DFD, and LDR sources must be resized below the wasm encoder's
1249
+ * ~12-Mpix source ceiling before encoding.
1250
+ */
1251
+ declare const ASSET_OPTIMIZE_VERSION = 1;
1252
+ declare const ASSET_RECEIPT_DIRECTORY: string;
1253
+ declare const ASSET_RECEIPT_FILE: string;
1254
+ type AssetKind = 'glb' | 'image' | 'hdr';
1255
+ type TexturePreset = 'color' | 'normal' | 'data' | 'hdr';
1256
+ type TextureEncodeMode = 'etc1s' | 'uastc' | 'uastc-hdr';
1257
+ interface AssetOptimizeOptions {
1258
+ /** Project root: anchors the encode cache and the receipt. */
1259
+ readonly root: string;
1260
+ /** Files (or directories) to optimize; may live outside the root. */
1261
+ readonly inputs: readonly string[];
1262
+ /** Output directory; defaults to each input's own directory. Resolved against the root. */
1263
+ readonly outDir?: string;
1264
+ /** LDR texture dimension cap (default 2048). */
1265
+ readonly maxDimension?: number;
1266
+ /** Equirect HDR dimension cap (default 4096); HDR inputs above it are refused, not resized. */
1267
+ readonly hdrMaxDimension?: number;
1268
+ /** `high` upgrades color slots to UASTC and raises encoder effort. */
1269
+ readonly quality?: 'default' | 'high';
1270
+ /** Standalone-image preset override; GLB textures always classify by slot. */
1271
+ readonly preset?: Exclude<TexturePreset, 'hdr'>;
1272
+ /** Re-encode even when the cache already has this input + settings. */
1273
+ readonly force?: boolean;
1274
+ readonly onProgress?: (message: string) => void;
1275
+ }
1276
+ interface TextureEncodeResult {
1277
+ readonly name: string;
1278
+ readonly slots: readonly string[];
1279
+ readonly preset: TexturePreset;
1280
+ readonly mode: TextureEncodeMode;
1281
+ readonly width: number;
1282
+ readonly height: number;
1283
+ readonly bytesIn: number;
1284
+ readonly bytesOut: number;
1285
+ readonly durationMs: number;
1286
+ }
1287
+ interface OptimizedAssetResult {
1288
+ readonly input: string;
1289
+ readonly output: string;
1290
+ readonly kind: AssetKind;
1291
+ readonly cached: boolean;
1292
+ readonly bytesIn: number;
1293
+ readonly bytesOut: number;
1294
+ readonly vramBytesIn: number;
1295
+ readonly vramBytesOut: number;
1296
+ readonly textures: readonly TextureEncodeResult[];
1297
+ readonly notes: readonly string[];
1298
+ }
1299
+ interface FailedAssetResult {
1300
+ readonly input: string;
1301
+ readonly error: string;
1302
+ }
1303
+ interface AssetOptimizeReport {
1304
+ readonly schemaVersion: 1;
1305
+ readonly command: 'assets optimize';
1306
+ readonly root: string;
1307
+ readonly results: readonly OptimizedAssetResult[];
1308
+ readonly failed: readonly FailedAssetResult[];
1309
+ readonly bytesIn: number;
1310
+ readonly bytesOut: number;
1311
+ }
1312
+ /** Classify a glTF texture by the material slots that reference it. */
1313
+ declare function presetForTextureSlots(slots: readonly string[]): Exclude<TexturePreset, 'hdr'>;
1314
+ /** Filename heuristic for standalone images; color is the safe default. */
1315
+ declare function presetForImageName(fileName: string): Exclude<TexturePreset, 'hdr'>;
1316
+ /** Optimize the given assets and commit the receipt last. */
1317
+ declare function optimizeAssets(options: AssetOptimizeOptions): Promise<AssetOptimizeReport>;
1318
+ declare function formatAssetOptimizeReport(report: AssetOptimizeReport): string;
1319
+ /**
1320
+ * Project assets the capture step optimizes automatically: every `.glb` and
1321
+ * `.hdr` under the public directory, excluding generated output (the
1322
+ * `three-blocks/` subtree, `.tb.glb` rewrites) and hidden directories.
1323
+ * Standalone images stay opt-in — a public directory legitimately holds
1324
+ * posters and favicons that must never become GPU textures.
1325
+ */
1326
+ declare function discoverPublicAssets(root: string, publicDir: string): Promise<readonly string[]>;
1327
+ type AssetState = 'fresh' | 'stale' | 'missing-input' | 'missing-output';
1328
+ interface AssetStatusEntry {
1329
+ readonly input: string;
1330
+ readonly output: string;
1331
+ readonly kind: AssetKind;
1332
+ readonly state: AssetState;
1333
+ }
1334
+ interface AssetStatusReport {
1335
+ readonly schemaVersion: 1;
1336
+ readonly command: 'assets status';
1337
+ readonly root: string;
1338
+ readonly state: 'fresh' | 'stale' | 'empty';
1339
+ readonly entries: readonly AssetStatusEntry[];
1340
+ }
1341
+ /** Re-verify every receipt entry against the bytes on disk. */
1342
+ declare function inspectAssets(root: string): Promise<AssetStatusReport>;
1343
+ declare function formatAssetStatusReport(report: AssetStatusReport): string;
1344
+
1345
+ export { ASSET_OPTIMIZE_VERSION, ASSET_RECEIPT_DIRECTORY, ASSET_RECEIPT_FILE, type AdoptedProjectStatus, type ArtifactState, type AssetKind, type AssetOptimizeOptions, type AssetOptimizeReport, type AssetState, type AssetStatusEntry, type AssetStatusReport, type AttachedPlaywrightShaderDriverOptions, type BrowserPreviewOptions, type BrowserPreviewReport, type BrowserPreviewRouteReport, type BrowserSmokeBuildReceipt, type BrowserSmokeCodec, type BrowserSmokeCodecRoute, type BrowserSmokeHmr, type BrowserSmokeHmrEvidence, type BrowserSmokeLifecycle, type BrowserSmokeOptions, type BrowserSmokeReport, type BrowserSmokeWorkerRestart, type BuiltinFontMetadata, type CaptureSceneResult, type CaptureShadersOptions, type CaptureShadersResult, DOCUMENTED_BROWSER_NOISE, type DiscoveredTextContent, type EncodedRoomEnvironment, type EnsuredShaderToolConfig, type EnvironmentBakeOptions, type EnvironmentBakeReport, type FailedAssetResult, type GenerateTextResult, type GeneratedTextAtlas, type GlyphSet, type GlyphSetOptions, type InferredShaderCaptureTarget, type InstalledShaderRuntime, type OptimizedAssetResult, type PlaywrightShaderDriverOptions, type ProjectMetadataV2, type ProjectStatus, RUNTIME_STATUS_RECEIPT_MAX_BYTES, RUNTIME_STATUS_RECEIPT_PATH, RUNTIME_STATUS_RECOVERY, type RoomEnvironmentCapture, type RoomEnvironmentCaptureOptions, type RoomEnvironmentCaptureResult, type RoomEnvironmentMetadata, type RuntimeAssetsStatus, type RuntimeBackend, type RuntimeCodecObservation, type RuntimeHmrCapabilities, type RuntimeHmrOutcome, type RuntimeHmrStatus, type RuntimeObservation, type RuntimeOptimizationStatus, type RuntimeState, type RuntimeStatsStatus, type RuntimeStatusReceiptV1, type RuntimeTextStatus, type RuntimeWorkerStatus, SHADER_ARTIFACT_DIRECTORY, SHADER_COMMITTED_TIMING_FILE, SHADER_DEVTOOLS_VERSION, SHADER_LOCAL_TIMING_FILE, SHADER_PARITY_DIRECTORY, SHADER_PARITY_EVIDENCE_FILE, SHADER_PROVIDER_TRANSFORM_VERSION, SHADER_TOOL_CONFIG_FILE, type ShaderBridgeConfig, type ShaderBrowserDriver, type ShaderBrowserObservation, type ShaderBrowserReadiness, type ShaderCapturePayload, type ShaderCaptureProvenance, type ShaderCaptureStageTiming, type ShaderCaptureStats, type ShaderCommittedTiming, type ShaderCoverage, type ShaderFreshness, type ShaderLocalTiming, type ShaderMatrixState, type ShaderMatrixStatus, type ShaderParityConfig, type ShaderParityEvidence, type ShaderParityResult, type ShaderReadinessConfig, type ShaderReceiptV2, type ShaderSceneArtifactStatus, type ShaderSceneConfig, type ShaderSceneParityEvidence, type ShaderSceneReceipt, type ShaderSceneTestResult, type ShaderSceneTiming, type ShaderScreenshotMetrics, type ShaderSkippedBuild, type ShaderStartupMeasurement, type ShaderState, type ShaderStatus, type ShaderTiming, type ShaderTimingEnvironment, type ShaderToolConfig, ShaderWatchQueue, type ShaderWatchQueueOptions, type ShaderWatchSession, type StaticPlaywrightShaderDriverOptions, TEXT_ATLAS_GENERATOR_VERSION, TEXT_ATLAS_SCHEMA_VERSION, type TestShadersOptions, type TestShadersResult, type TextAtlasFileReceipt, type TextAtlasGenerationReceipt, type TextAtlasInspection, type TextAtlasRuntimeOptions, type TextAtlasStatus, type TextState, type TextStatus, type TextUnicodePreset, type TextUnicodeRange, type TextureEncodeMode, type TextureEncodeResult, type TexturePreset, type ThreeBlocksStatus, type ToolTextConfiguration, type ToolTextFontRoute, type ToolTextFontSource, type ToolTextGenerationOptions, type ValidateShaderCaptureOptions, type ValidatedShaderCapture, type WatchShadersOptions, type WriteShaderArtifactsOptions, type WrittenShaderArtifacts, bakeRoomEnvironment, buildGlyphSet, builtinFontMetadata, captureRoomEnvironment, captureShaders, compareShaderScreenshots, createAttachedPlaywrightShaderDriver, createPlaywrightShaderDriver, createStaticPlaywrightShaderDriver, discoverPublicAssets, discoverTextContent, encodeRoomEnvironmentKtx2, ensureShaderToolConfig, extractCanvasTextFromHtml, extractDefinedTextContent, findProjectRoot, findShaderProjectRoot, formatAssetOptimizeReport, formatAssetStatusReport, formatBrowserPreviewReport, formatBrowserSmokeReport, formatEnvironmentBakeReport, formatShaderMatrixStatus, formatStatus, formatThreeBlocksStatus, generateTextAtlases, inferShaderCaptureTarget, inspectAssets, inspectProject, inspectShaderMatrix, inspectTextAtlases, inspectThreeBlocksStatus, isDocumentedBrowserNoise, isRetryableShaderObservationNavigation, loadShaderCaptureRuntimeSource, loadShaderToolConfig, optimizeAssets, parseBrowserSmokeLifecycle, parseProjectMetadata, parseRuntimeStatusReceipt, parseShaderReceipt, parseShaderToolConfig, parseTextAtlasReceipt, parseTextConfiguration, presetForImageName, presetForTextureSlots, prewarmBuiltinFont, readInstalledShaderRuntime, readShaderCommittedTiming, readShaderLocalTiming, readShaderParityEvidence, readShaderTiming, readTextConfiguration, runBrowserPreview, runBrowserSmoke, screenshotLuminance, serializeShaderManifest, shaderCoverage, shaderScreenshotMetrics, snapshotShaderInputs, stableStringify, testShaders, threeBlocksStatusIsFresh, validateEnvironmentCubeSize, validateShaderCapture, watchShaders, webgpuBrowserArgs, webgpuBrowserHeadless, writeShaderArtifacts, writeShaderLocalTiming, writeShaderParityEvidence, writeShaderTimingTwins };