@dcl-regenesislabs/web-editor 0.3.22-41162201857.commit-2a300c0 → 0.3.22-41162568497.commit-968353b

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.
package/editor.js CHANGED
@@ -34590,7 +34590,7 @@ module.exports = require('./index')
34590
34590
  `,tqt="web",hZe=/^[a-z0-9][a-z0-9-]{0,63}$/,DSr=/^[A-Za-z0-9_.]{1,64}$/,RSr="\0";function nqt(t){let n="";for(const r of new TextEncoder().encode(t))n+=String.fromCharCode(r);return btoa(n)}function rqt(t){if(!hZe.test(t))throw new Error(`Invalid preview project id "${t}"`);return`/preview/${t}`}function iqt(t){if(!DSr.test(t))throw new Error(`Invalid preview machine id "${t}"`);return"-"+t}const _Ze=(t,n=tqt)=>"b64-"+nqt(rqt(t)+iqt(n)),MSr=(t,n,r,a=tqt)=>"b64-"+nqt(`${rqt(t)}/${n}${RSr}${r}${iqt(a)}`),FTe=t=>t;async function LSr(t){const n=new Uint8Array(await crypto.subtle.digest("SHA-256",FTe(t)));return String(n.slice(0,6).reduce((r,a)=>r*256+a,0))}const FSr="https://registry.npmjs.org",ZWe=1024*1024,jSr={packages:200,unpackedBytes:100*ZWe,tarballBytes:20*ZWe};class Bh extends Error{constructor(n,r,a){super(r),this.code=n,this.pkg=a,this.name="PackageResolveError"}}const S9e=t=>t instanceof Error?t.message:String(t);function Kbe(t){const n={};if(v_(t))for(const[r,a]of Object.entries(t))typeof a=="string"&&(n[r]=a);return n}function KWe(t,n){return t<n?-1:t>n?1:0}const Rve=t=>Object.entries(t).sort(([n],[r])=>KWe(n,r));function Ine(t){try{return new URL(t).origin}catch{throw new Bh("invalid",`"${t}" is not a url`)}}function T9e(t,n){let r=t,a=n.trim();if(a.startsWith("npm:")){const s=a.lastIndexOf("@");r=s>4?a.slice(4,s):a.slice(4),a=s>4?a.slice(s+1):"latest"}return/^(file|link|portal):/.test(a)?{kind:"local"}:/^[a-z+]+:/.test(a)||a.startsWith("git@")||a.includes("/")||!mae(r)?{kind:"unsupported"}:{kind:"registry",name:r,range:a===""?"*":a}}function MOt(t,n){const r=v_(n)&&v_(n.dist)?n.dist:void 0;if(!v_(n)||r===void 0||typeof r.tarball!="string")throw new Bh("invalid",`the registry has no tarball for ${t}`,t);const a=Kbe(n.optionalDependencies),s=Kbe(n.dependencies);for(const x of Object.keys(a))delete s[x];const l=v_(n.peerDependenciesMeta)?n.peerDependenciesMeta:{},f={};for(const[x,T]of Object.entries(Kbe(n.peerDependencies))){const A=l[x];!(v_(A)&&A.optional===!0)&&s[x]===void 0&&a[x]===void 0&&(f[x]=T)}const m=n.bundleDependencies??n.bundledDependencies,_=typeof r.integrity=="string"?r.integrity.split(/\s+/).find(x=>x.startsWith("sha512-")):void 0;return{tarball:r.tarball,integrity:_==null?void 0:_.split("?")[0],shasum:typeof r.shasum=="string"?r.shasum.toLowerCase():void 0,dependencies:s,optional:a,peers:f,bundled:m===!0?"all":new Set(Array.isArray(m)?m.filter(x=>typeof x=="string"):[]),platform:n.os!==void 0||n.cpu!==void 0||n.libc!==void 0}}async function LOt(t,n){return new Uint8Array(await crypto.subtle.digest(t,FTe(n)))}function BSr(t){let n="";for(const r of t)n+=String.fromCharCode(r);return btoa(n)}const USr=t=>Array.from(t,n=>n.toString(16).padStart(2,"0")).join("");function $Sr(t){const n=t.url.replace(/\/+$/,""),r=Ine(n),a=new Map;let s=0;async function l(_,x){s++;let T;try{T=await t.fetch(_,{headers:x===void 0?void 0:{Accept:x},credentials:"omit",signal:t.signal})}catch(A){throw new Bh("network",`${_}: ${S9e(A)}`)}if(T.url!==""&&Ine(T.url)!==r)throw new Bh("registry-origin",`${_} was answered by ${Ine(T.url)}, not by ${r}`);return T}async function f(_){if(t.frozen)throw new Bh("frozen",`${_} is not pinned by the lock, and the project is frozen`,_);const x=await l(`${n}/${_.replace("/","%2f")}`,"application/vnd.npm.install-v1+json");if(x.status===404)throw new Bh("not-found",`${_} is not in the registry`,_);if(!x.ok)throw new Bh("network",`${_}: the registry answered ${x.status}`,_);let T;try{T=await x.json()}catch(A){throw new Bh("network",`${_}: ${S9e(A)}`,_)}if(!v_(T)||!v_(T.versions))throw new Bh("invalid",`${_}: the registry sent no versions`,_);return{tags:Kbe(T["dist-tags"]),versions:T.versions}}async function m(_,x,T){var N;const A=new Bh("limit",`${x}: the tarball is larger than ${T} bytes`,x);if(Number(_.headers.get("content-length"))>T)throw A;try{const L=(N=_.body)==null?void 0:N.getReader();if(L===void 0){const te=new Uint8Array(await _.arrayBuffer());if(te.length>T)throw A;return te}const B=[];let G=0;for(;;){const{done:te,value:pe}=await L.read();if(te)break;if(G+=pe.length,G>T)throw await L.cancel(),A;B.push(pe)}const Y=new Uint8Array(G);let Z=0;for(const te of B)Y.set(te,Z),Z+=te.length;return Y}catch(L){throw L instanceof Bh?L:new Bh("network",`${x}: ${S9e(L)}`,x)}}return{url:n,requests:()=>s,packument(_){let x=a.get(_);return x===void 0&&a.set(_,x=f(_)),x},async tarball(_,x,T){if(t.frozen)throw new Bh("frozen",`${_} is not in the package store, and the project is frozen`,_);if(Ine(x)!==r)throw new Bh("registry-origin",`${_}: its tarball is on ${Ine(x)}, not on ${r}`,_);const A=await l(x);if(!A.ok)throw new Bh("network",`${_}: the registry answered ${A.status}`,_);return m(A,_,T)}}}const zSr=new Set("assert async_hooks buffer child_process cluster console constants crypto dgram diagnostics_channel dns domain events fs http http2 https inspector module net os path perf_hooks process punycode querystring readline repl stream string_decoder sys timers tls trace_events tty url util v8 vm wasi worker_threads zlib".split(" ")),VSr=t=>t.startsWith("node:")||zSr.has(t.split("/",1)[0]),WSr=new RegExp(`(?<![.\\w$])require\\(\\s*['"]([^'"\\n]+)['"]\\s*\\)|\\b(?:import|export)\\b[^'"\\n;]*?\\bfrom\\s*['"]([^'"\\n]+)['"]|\\bimport\\s*\\(?\\s*['"]([^'"\\n]+)['"]`,"g"),qSr=/\.(js|mjs|cjs|json)$/,GSr=2e3,HSr=1024*1024;function JSr(t){const n=t.get("package.json");if(n===void 0)return{};try{const r=JSON.parse(new TextDecoder().decode(n));return v_(r)?r:{}}catch{return{}}}function QSr(t,n){const r=new hSr,a="/node_modules/"+t;for(const[x,T]of n)qSr.test(x)&&T.length<=HSr&&r.set(`${a}/${x}`,T);const s=new TSr({vfs:r}),l=s.resolve(t,"/","import"),f=new Set,m=new Set,_=l.kind==="file"?[l.path]:[];for(let x=_.pop();x!==void 0&&m.size<GSr;x=_.pop())if(!(m.has(x)||x.endsWith(".json"))){m.add(x);for(const T of(r.text(x)??"").matchAll(WSr)){const A=T[1]??T[2]??T[3],N=s.resolve(A,Xj(x),T[1]===void 0?"import":"require");N.kind==="file"?_.push(N.path):N.kind==="missing"&&VSr(A)&&f.add(A.replace(/^node:/,"").split("/",1)[0])}}return[...f].sort()}function YSr(t,n){const r=JSr(n),a=v_(r.scripts)?r.scripts:{},s=n.has("binding.gyp")||r.gypfile===!0,l=["preinstall","install","postinstall"].filter(f=>typeof a[f]=="string");return s&&!l.includes("install")&&l.push("install"),{installScripts:l,native:s||[...n.keys()].some(f=>f.endsWith(".node")),nodeBuiltins:QSr(t,n)}}class VC extends Error{constructor(n,r){super(r),this.reason=n}}const sq=512,Hxe=new TextDecoder;async function XSr(t,n){const a=new Blob([FTe(t)]).stream().pipeThrough(new DecompressionStream("gzip")).getReader(),s=[];let l=0;try{for(;;){const{done:_,value:x}=await a.read();if(_)break;if(l+=x.length,l>n)throw await a.cancel(),new VC("too-large",`unpacks to more than ${n} bytes`);s.push(x)}}catch(_){throw _ instanceof VC?_:new VC("malformed","is not a gzip archive")}const f=new Uint8Array(l);let m=0;for(const _ of s)f.set(_,m),m+=_.length;return f}function Xre(t,n,r){let a=n;for(;a<n+r&&t[a]!==0;)a++;return Hxe.decode(t.subarray(n,a))}function ZSr(t){if(t[124]&128)throw new VC("too-large","holds an entry larger than 8 GB");const n=parseInt(Xre(t,124,12).trim()||"0",8);if(!Number.isSafeInteger(n)||n<0)throw new VC("malformed","has an entry with no valid size");return n}function KSr(t){let n=0;for(let r=0;r<sq;r++)n+=r>=148&&r<156?32:t[r];return n===parseInt(Xre(t,148,8).trim(),8)}function eTr(t){const n={};for(let r=0;r<t.length;){let a=r;for(;a<t.length&&t[a]!==32;)a++;const s=Number(Hxe.decode(t.subarray(r,a)));if(!Number.isSafeInteger(s)||s<=0)break;const l=Hxe.decode(t.subarray(a+1,r+s-1)),f=l.indexOf("=");f>0&&(n[l.slice(0,f)]=l.slice(f+1)),r+=s}return n}function tTr(t){if(t.includes("\0"))throw new VC("unsafe-path","has an entry with a NUL in its path");const n=t.split(/[\\/]/);if(t.startsWith("/")||t.startsWith("\\")||/^[A-Za-z]:/.test(t))throw new VC("unsafe-path",`has an entry with an absolute path: ${t}`);if(n.includes(".."))throw new VC("unsafe-path",`has an entry that leaves the package: ${t}`);const r=n.filter(a=>a!==""&&a!==".").slice(1);return r.length===0?void 0:r.join("/")}function nTr(t){const n=new Map,r=[];let a,s={};for(let l=0;l+sq<=t.length;){const f=t.subarray(l,l+sq);if(f.every(B=>B===0))break;if(!KSr(f))throw new VC("malformed","is not a tar archive");const m=String.fromCharCode(f[156]||48),_=s.size===void 0?ZSr(f):Number(s.size),x=l+sq;if(!Number.isSafeInteger(_)||x+_>t.length)throw new VC("malformed","is cut short");const T=t.subarray(x,x+_);if(l=x+Math.ceil(_/sq)*sq,m==="x"){s=eTr(T);continue}if(m==="g")continue;if(m==="L"){a=Hxe.decode(T).replace(/\0+$/,"");continue}if(m==="K")continue;const A=Xre(f,257,5)==="ustar"?Xre(f,345,155):"",N=s.path??a??(A===""?"":A+"/")+Xre(f,0,100);a=void 0,s={};const L=tTr(N);if(!(m==="5"||L===void 0)){if(m!=="0"&&m!=="7"){r.push(L);continue}n.set(L,T.slice())}}return{files:n,skipped:r}}const rTr=4;async function iTr(t){var Wt;const n=$Sr({url:t.registry??FSr,fetch:t.fetch??(($e,Me)=>fetch($e,Me)),frozen:t.frozen===!0,signal:t.signal}),r={...jSr,...t.limits},a=((Wt=t.snapshot)==null?void 0:Wt.provided)??{},s=t.lock!==void 0&&t.lock.registry===n.url?t.lock:void 0,l={packages:0,downloaded:0,reused:0,requests:0,tarballBytes:0,unpackedBytes:0},f=[],m=($e,Me,$t)=>{f.some(vt=>vt.code===$e&&vt.message===Me)||f.push({code:$e,message:Me,...$t===void 0?{}:{package:$t}})},_=new Map,x=new Map,T=new Map,A=[],N={};async function L($e){if(_.has($e)||x.has($e)||(s==null?void 0:s.packages[$e])!==void 0)return;const Me=$q($e);if(Me===void 0)throw new Bh("invalid",`"${$e}" is not a package version`);const $t=(await n.packument(Me.name)).versions[Me.version];if($t===void 0)throw new Bh("no-version",`${$e} is not in the registry`,$e);x.set($e,MOt($e,$t))}async function B($e,Me,$t){const vt=!Dve(Me),wt=Ke=>{const st=$q(Ke);return st!==void 0&&st.name===$e&&(vt||aq(st.version,Me))},dt=Ke=>_.has(Ke)||(s==null?void 0:s.packages[Ke])!==void 0;if($t!==void 0&&wt($t)&&dt($t))return $t;if(!vt){const Ke=tn=>{const Cn=NOt([...tn].filter(wt).map(un=>un.slice($e.length+1)),Me);return Cn===void 0?void 0:`${$e}@${Cn}`},st=Ke(_.keys())??Ke(Object.keys((s==null?void 0:s.packages)??{}));if(st!==void 0)return st}const Nt=await n.packument($e),Ut=Nt.tags.latest,qt=vt?Nt.tags[Me]:Ut!==void 0&&aq(Ut,Me)?Ut:NOt(Object.keys(Nt.versions),Me);if(qt===void 0||kG(qt)===void 0||Nt.versions[qt]===void 0)throw new Bh("no-version",`no version of ${$e} matches "${Me}"`,$e);const Re=`${$e}@${qt}`;return!_.has(Re)&&!x.has(Re)&&x.set(Re,MOt(Re,Nt.versions[qt])),Re}function G($e,Me){var wt;if(_.has($e))return;if(_.size>=r.packages)throw new Bh("limit",`the project needs more than ${r.packages} packages`);const $t=x.get($e),vt=s==null?void 0:s.packages[$e];if($t!==void 0)_.set($e,{resolved:$t.tarball,integrity:$t.integrity??"",dependencies:{}});else if(vt!==void 0)_.set($e,{resolved:vt.resolved,integrity:vt.integrity,dependencies:{...vt.dependencies}});else throw new Bh("invalid",`${$e} was never resolved`,$e);T.set($e,Me),A.push($e),(wt=t.onProgress)==null||wt.call(t,{type:"resolved",package:$e})}function Y($e,Me){const $t=T9e($e,Me);if(t.frozen===!0||a[$e]!==void 0||$t.kind!=="registry")return;const vt=wt=>wt.startsWith($t.name+"@")&&Dve($t.range)&&aq(wt.slice($t.name.length+1),$t.range);[..._.keys(),...Object.keys((s==null?void 0:s.packages)??{})].some(vt)||n.packument($t.name).catch(()=>{})}function Z($e,Me,$t){const vt=a[$e];if(vt===void 0)return!1;const wt=Me.trim();return vt!==""&&Dve(wt)&&!aq(vt,wt,{includePrerelease:!0})&&($e==="@dcl/sdk"&&$t===void 0?m("sdk-version",`This project pins @dcl/sdk ${wt}; it is built here with ${vt}.`,$e):m("snapshot-version",`${$t??"This project"} asks for ${$e} ${wt}; it gets the ${vt} the SDK ships.`,$t??$e)),!0}function te($e,Me){var wt,dt;const $t=T.get(Me),vt=($t===void 0||(dt=(wt=_.get($t))==null?void 0:wt.dependencies)==null?void 0:dt[$e])??N[$e];return vt===void 0?void 0:Zj($e,vt)}async function pe($e){var Ut;const Me=_.get($e);if(Me===void 0)return;const $t=Me.dependencies??(Me.dependencies={}),vt=x.get($e);if(vt===void 0){for(const[qt,Re]of Rve({...$t})){if(a[qt]!==void 0){delete $t[qt];continue}const Ke=Zj(qt,Re);await L(Ke),G(Ke,$e)}return}const wt=[];for(const[qt,Re]of Object.entries(vt.dependencies))wt.push([qt,Re,"required"]);for(const[qt,Re]of Object.entries(vt.optional))wt.push([qt,Re,"optional"]);for(const[qt,Re]of Object.entries(vt.peers))wt.push([qt,Re,"peer"]);wt.sort(([qt],[Re])=>KWe(qt,Re));const dt=qt=>vt.bundled==="all"||vt.bundled.has(qt);for(const[qt,Re,Ke]of wt)Ke!=="peer"&&!dt(qt)&&Y(qt,Re);let Nt=0;for(const[qt,Re,Ke]of wt){if(Ke!=="peer"&&dt(qt)||Z(qt,Re,$e))continue;const st=T9e(qt,Re);if(st.kind!=="registry"){m("unsupported-spec",`${$e} depends on ${qt} as "${Re}", which is not a registry version; it was left out.`,$e);continue}try{let tn=Ke==="peer"?te(qt,$e):void 0;if(tn===void 0?tn=await B(st.name,st.range,void 0):Dve(st.range)&&!aq(tn.slice(tn.lastIndexOf("@")+1),st.range)&&m("peer-unmet",`${$e} expects ${qt} ${st.range}; the project uses ${tn}.`,$e),Ke==="optional"&&((Ut=x.get(tn))==null?void 0:Ut.platform)===!0&&!_.has(tn)){Nt++;continue}G(tn,$e),$t[qt]=DOt(qt,tn)}catch(tn){if(!(tn instanceof Bh)||tn.code==="limit"||tn.code==="frozen"||Ke==="required")throw tn;m(Ke==="peer"?"peer-unmet":"optional-skipped",`${$e}: ${qt} was left out (${tn.message}).`,$e)}}Nt>0&&m("optional-skipped",`${$e}: ${Nt} optional packages built for one platform were left out.`,$e)}for(const[$e,Me]of Object.entries(t.dependencies))typeof Me=="string"&&mae($e)&&(s==null?void 0:s.dependencies[$e])===void 0&&Y($e,Me);for(const[$e,Me]of Rve(t.dependencies)){if(typeof Me!="string"||Z($e,Me,void 0))continue;const $t=T9e($e,Me);if($t.kind==="local")continue;if($t.kind==="unsupported"||!mae($e)){m("unsupported-spec",`${$e}: "${Me}" is not a registry version; it was left out.`,$e);continue}const vt=s==null?void 0:s.dependencies[$e],wt=await B($t.name,$t.range,vt===void 0?void 0:Zj($e,vt));G(wt,void 0),N[$e]=DOt($e,wt)}for(const[$e,Me]of Object.entries(t.devDependencies??{}))typeof Me=="string"&&Z($e,Me,void 0);for(let $e=A.shift();$e!==void 0;$e=A.shift())await pe($e);const fe=($e,Me)=>{if(l.unpackedBytes+=Me,l.unpackedBytes>r.unpackedBytes)throw new Bh("limit",`the packages unpack to more than ${r.unpackedBytes} bytes (at ${$e})`,$e)};async function X($e,Me){var qt,Re;const $t=await n.tarball($e,Me.resolved,r.tarballBytes),vt="sha512-"+BSr(await LOt("SHA-512",$t));if(Me.integrity===""){const Ke=(qt=x.get($e))==null?void 0:qt.shasum;if(Ke===void 0||USr(await LOt("SHA-1",$t))!==Ke)throw new Bh("integrity",`${$e}: the tarball does not match the checksum the registry lists`,$e);Me.integrity=vt,m("weak-integrity",`${$e} was published with a SHA-1 checksum only; the lock pins what was downloaded.`,$e)}else if(vt!==Me.integrity)throw new Bh("integrity",`${$e}: the tarball does not match its integrity (${Me.integrity})`,$e);(Re=t.onProgress)==null||Re.call(t,{type:"downloaded",package:$e,bytes:$t.length});let wt;try{wt=nTr(await XSr($t,2*r.unpackedBytes+ZWe))}catch(Ke){throw Ke instanceof VC?new Bh(Ke.reason==="too-large"?"limit":"unsafe-tarball",`${$e} ${Ke.message}`,$e):Ke}const{files:dt}=wt;dt.delete(MTe);let Nt=0;for(const Ke of dt.values())Nt+=Ke.length;fe($e,Nt);const Ut={integrity:Me.integrity,files:dt.size,bytes:Nt,...YSr($e.slice(0,$e.lastIndexOf("@")),dt)};await t.store.remove($e);for(const[Ke,st]of dt)await t.store.write($e,Ke,st);return await _Sr(t.store,$e,Ut),wt.skipped.length>0&&m("links-skipped",`${$e}: ${wt.skipped.length} links in the tarball were left out.`,$e),l.downloaded++,l.tarballBytes+=$t.length,Ut}const he=[..._.keys()],Ee=new Map;let qe=0,xt=!1;const lt=async()=>{var $e;for(;qe<he.length&&!xt;){const Me=he[qe++],$t=_.get(Me);if($t!==void 0)try{const vt=await JWt(t.store,Me),wt=vt!==void 0&&vt.integrity===$t.integrity;wt&&(fe(Me,vt.bytes),l.reused++),Ee.set(Me,wt?vt:await X(Me,$t)),($e=t.onProgress)==null||$e.call(t,{type:"stored",package:Me,reused:wt,done:Ee.size,total:he.length})}catch(vt){throw xt=!0,vt}}};await Promise.all(Array.from({length:Math.min(rTr,he.length)},lt));const St=new Set(Object.entries(N).map(([$e,Me])=>Zj($e,Me)));for(const $e of he){const Me=Ee.get($e);Me!==void 0&&(Me.installScripts.length>0&&m("install-script",`${$e} declares ${Me.installScripts.join(", ")} scripts; they are not run here.`,$e),Me.native&&m("native",`${$e} ships native code, which a scene cannot load.`,$e),Me.nodeBuiltins.length>0&&St.has($e)&&m("node-builtins",`${$e} uses Node built-ins a scene does not have: ${Me.nodeBuiltins.join(", ")}.`,$e))}const ht=($e,Me)=>{const[$t,vt]=[$q($e),$q(Me)];if($t===void 0||vt===void 0||$t.name!==vt.name)return KWe($e,Me);const[wt,dt]=[kG($t.version),kG(vt.version)];return wt===void 0||dt===void 0?0:mZe(wt,dt)},Ue={};for(const $e of[...he].sort(ht)){const Me=_.get($e);if(Me===void 0)continue;const $t=Object.fromEntries(Rve(Me.dependencies??{}));Ue[$e]={resolved:Me.resolved,integrity:Me.integrity,...Object.keys($t).length>0?{dependencies:$t}:{}}}return l.packages=he.length,l.requests=n.requests(),{lock:{lockfileVersion:1,registry:n.url,dependencies:Object.fromEntries(Rve(N)),packages:Ue},warnings:f,stats:l}}var FOt={};function jOt(t){return Array.isArray(t)?t:[t]}const eqe="",BOt=" ",k9e="\\",aTr=/^\s+$/,sTr=/(?:[^\\]|^)\\$/,oTr=/^\\!/,cTr=/^\\#/,lTr=/\r?\n/g,uTr=/^\.*\/|^\.+$/,E9e="/";let aqt="node-ignore";typeof Symbol<"u"&&(aqt=Symbol.for("node-ignore"));const UOt=aqt,dTr=(t,n,r)=>Object.defineProperty(t,n,{value:r}),fTr=/([0-z])-([0-z])/g,sqt=()=>!1,pTr=t=>t.replace(fTr,(n,r,a)=>r.charCodeAt(0)<=a.charCodeAt(0)?n:eqe),mTr=t=>{const{length:n}=t;return t.slice(0,n-n%2)},hTr=[[/^\uFEFF/,()=>eqe],[/((?:\\\\)*?)(\\?\s+)$/,(t,n,r)=>n+(r.indexOf("\\")===0?BOt:eqe)],[/(\\+?)\s/g,(t,n)=>{const{length:r}=n;return n.slice(0,r-r%2)+BOt}],[/[\\$.|*+(){^]/g,t=>`\\${t}`],[/(?!\\)\?/g,()=>"[^/]"],[/^\//,()=>"^"],[/\//g,()=>"\\/"],[/^\^*\\\*\\\*\\\//,()=>"^(?:.*\\/)?"],[/^(?=[^^])/,function(){return/\/(?!$)/.test(this)?"^":"(?:^|\\/)"}],[/\\\/\\\*\\\*(?=\\\/|$)/g,(t,n,r)=>n+6<r.length?"(?:\\/[^\\/]+)*":"\\/.+"],[/(^|[^\\]+)(\\\*)+(?=.+)/g,(t,n,r)=>{const a=r.replace(/\\\*/g,"[^\\/]*");return n+a}],[/\\\\\\(?=[$.|*+(){^])/g,()=>k9e],[/\\\\/g,()=>k9e],[/(\\)?\[([^\]/]*?)(\\*)($|\])/g,(t,n,r,a,s)=>n===k9e?`\\[${r}${mTr(a)}${s}`:s==="]"&&a.length%2===0?`[${pTr(r)}${a}]`:"[]"],[/(?:[^*])$/,t=>/\/$/.test(t)?`${t}$`:`${t}(?=$|\\/$)`],[/(\^|\\\/)?\\\*$/,(t,n)=>`${n?`${n}[^/]+`:"[^/]*"}(?=$|\\/$)`]],$Ot=Object.create(null),_Tr=(t,n)=>{let r=$Ot[t];return r||(r=hTr.reduce((a,[s,l])=>a.replace(s,l.bind(t)),t),$Ot[t]=r),n?new RegExp(r,"i"):new RegExp(r)},gZe=t=>typeof t=="string",gTr=t=>t&&gZe(t)&&!aTr.test(t)&&!sTr.test(t)&&t.indexOf("#")!==0,yTr=t=>t.split(lTr);class vTr{constructor(n,r,a,s){this.origin=n,this.pattern=r,this.negative=a,this.regex=s}}const bTr=(t,n)=>{const r=t;let a=!1;t.indexOf("!")===0&&(a=!0,t=t.substr(1)),t=t.replace(oTr,"!").replace(cTr,"#");const s=_Tr(t,n);return new vTr(r,t,a,s)},xTr=(t,n)=>{throw new n(t)},d3=(t,n,r)=>gZe(t)?t?d3.isNotRelative(t)?r(`path should be a \`path.relative()\`d string, but got "${n}"`,RangeError):!0:r("path must not be empty",TypeError):r(`path must be a string, but got \`${n}\``,TypeError),oqt=t=>uTr.test(t);d3.isNotRelative=oqt;d3.convert=t=>t;class wTr{constructor({ignorecase:n=!0,ignoreCase:r=n,allowRelativePaths:a=!1}={}){dTr(this,UOt,!0),this._rules=[],this._ignoreCase=r,this._allowRelativePaths=a,this._initCache()}_initCache(){this._ignoreCache=Object.create(null),this._testCache=Object.create(null)}_addPattern(n){if(n&&n[UOt]){this._rules=this._rules.concat(n._rules),this._added=!0;return}if(gTr(n)){const r=bTr(n,this._ignoreCase);this._added=!0,this._rules.push(r)}}add(n){return this._added=!1,jOt(gZe(n)?yTr(n):n).forEach(this._addPattern,this),this._added&&this._initCache(),this}addPattern(n){return this.add(n)}_testOne(n,r){let a=!1,s=!1;return this._rules.forEach(l=>{const{negative:f}=l;if(s===f&&a!==s||f&&!a&&!s&&!r)return;l.regex.test(n)&&(a=!f,s=f)}),{ignored:a,unignored:s}}_test(n,r,a,s){const l=n&&d3.convert(n);return d3(l,n,this._allowRelativePaths?sqt:xTr),this._t(l,r,a,s)}_t(n,r,a,s){if(n in r)return r[n];if(s||(s=n.split(E9e)),s.pop(),!s.length)return r[n]=this._testOne(n,a);const l=this._t(s.join(E9e)+E9e,r,a,s);return r[n]=l.ignored?l:this._testOne(n,a)}ignores(n){return this._test(n,this._ignoreCache,!1).ignored}createFilter(){return n=>!this.ignores(n)}filter(n){return jOt(n).filter(this.createFilter())}test(n){return this._test(n,this._testCache,!0)}}const Jxe=t=>new wTr(t),STr=t=>d3(t&&d3.convert(t),t,sqt);Jxe.isPathValid=STr;Jxe.default=Jxe;var TTr=Jxe;if(typeof process<"u"&&(FOt&&FOt.IGNORE_TEST_WIN32||process.platform==="win32")){const t=r=>/^\\\\\?\\/.test(r)||/["<>|\u0000-\u001F]+/u.test(r)?r:r.replace(/\\/g,"/");d3.convert=t;const n=/^[a-z]:\//i;d3.isNotRelative=r=>n.test(r)||oqt(r)}const kTr=ewe(TTr),ETr=[".*","package.json","package-lock.json","yarn-lock.json","build.json","export","tsconfig.json","tslint.json","node_modules","dclcontext","**/*.ts","**/*.tsx","Dockerfile","thumbnails","dist","README.md","*.blend","*.fbx","*.zip","*.rar","*.map","node_modules/**","*.md"];function yZe(t,n){const r=kTr().add([...(n==null?void 0:n.split(`
34591
34591
  `))??[],...ETr].filter(Boolean)),a=[...t].filter(s=>!s.split("/").some(l=>l.startsWith(".")));return r.filter(a)}function ATr(t){const n=t.scene;if(!v_(n))throw new Error('scene.json has no "scene" section');const{base:r,parcels:a}=n;if(typeof r!="string"||!Array.isArray(a))throw new Error("scene.json needs scene.base and scene.parcels");return[...new Set([r,...a.filter(s=>typeof s=="string")])]}function CTr(t){const n=new Set,r=t.files.map(({path:a,version:s})=>{const l=a.toLowerCase();if(n.has(l))throw new Error(`Two files deploy as "${l}"; scene file names are case-insensitive`);return n.add(l),{file:l,hash:MSr(t.projectId,a,s,t.machineId)}});return{version:"v3",type:"scene",id:_Ze(t.projectId,t.machineId),pointers:ATr(t.sceneJson),timestamp:t.timestamp,content:r,metadata:t.sdkVersion===void 0?t.sceneJson:{sdkVersion:t.sdkVersion,...t.sceneJson}}}const Qxe="dcl-editor-preview-v1",OTr="dcl-editor-storage-v1";function tqe(t,n){if(!hZe.test(n))throw new Error(`Invalid preview project id "${n}"`);return new URL(`preview/${n}/`,t).href}const nqe=t=>t+"__manifest",zOt=(t,n)=>`${t}content/contents/${n}`;async function PTr(t){const n=new URL(t.realmUrl).href;if(n!==tqe(new URL("../../",n),t.projectId))throw new Error(`"${t.realmUrl}" is not the preview realm of project "${t.projectId}"`);const r=new Map([...t.files,...t.buildOutputs]),a=r.get(".dclignore"),s=yZe(r.keys(),a===void 0?void 0:new TextDecoder().decode(a)),l=await Promise.all(s.map(async B=>{const G=r.get(B)??new Uint8Array;return{path:B,bytes:G,version:await LSr(G)}})),f=await caches.open(Qxe),m=await f.match(nqe(n)),_=await(m==null?void 0:m.json().catch(()=>{})),x=v_(_)&&typeof _.version=="number"?_.version:0,T=CTr({projectId:t.projectId,sceneJson:t.sceneJson,files:l,timestamp:Date.now(),sdkVersion:t.sdkVersion,machineId:t.machineId}),A=new Set;await Promise.all(T.content.map(async({hash:B},G)=>{const Y=zOt(n,B);A.add(new URL(Y).href),await f.match(Y)===void 0&&await f.put(Y,new Response(FTe(l[G].bytes),{headers:{"Content-Type":"application/octet-stream"}}))}));const N={version:x+1,entity:T};await f.put(nqe(n),new Response(JSON.stringify(N),{headers:{"Content-Type":"application/json"}}));const L=zOt(n,"");for(const B of await f.keys())B.url.startsWith(L)&&!A.has(B.url)&&await f.delete(B);return{realmUrl:n,entityId:T.id}}const ITr="0.1.0";function NTr(t){try{return JSON.parse(t)}catch{throw new Error("Invalid scene request.")}}function DTr(t){const n=zXe({paths:async()=>[...t.files().keys()],read:async r=>t.files().get(r),write:(r,a,s,l)=>t.write(r,a,s,l),...t.written===void 0?{}:{written:t.written}});return async(r,a)=>{a.throwIfAborted();const{tool:s,args:l}=r;if(s==="playtest"){if(t.playtest===void 0)throw new Error("Playtesting requires an updated editor.");return JSON.stringify(await t.playtest(l,a))}if(s==="ui_layout"){if(t.uiLayout===void 0)throw new Error("Measuring the UI requires an updated editor.");return JSON.stringify(await t.uiLayout(l,a))}if(s==="mobile_check"){if(t.uiLayout===void 0)throw new Error("The mobile check requires an updated editor.");return JSON.stringify(await t.uiLayout({check:!0},a,async()=>t.files()))}if(s==="asset_request"){if(t.assets===void 0)throw new Error("Asset tools require an updated editor.");return JSON.stringify(await t.assets(l,a))}if(s==="build")return JSON.stringify(await t.build(a));if(s==="view")return JSON.stringify(await t.view(_Vt(l),a));if(s==="scene_request")return JSON.stringify(await t.scene(typeof l.request=="string"?NTr(l.request):l.request,a));if(!fzt(s))throw new Error("Unknown browser tool.");return n(s,l,a)}}function RTr(t){const n=new Set;let r=null,a=null,s=0,l=null,f=Promise.resolve(),m=null;const _=X=>{for(const he of n)he(X)},x=X=>[{id:"claude",label:"Claude",available:!1,models:[],defaultModel:"",transport:"service",reason:X}],T=X=>{(r==null?void 0:r.readyState)===WebSocket.OPEN&&r.send(JSON.stringify(X))};function A(X,he,Ee="stopped"){var xt;const qe=l;qe!==null&&(l=null,qe.controller.abort(),(xt=qe.ended)==null||xt.call(qe).catch(lt=>console.error("[assistant] ending the turn failed",lt)),he!==void 0&&_({kind:"error",turnId:qe.id,message:he}),_({kind:"done",turnId:qe.id,ok:X}),tw(X?"prompt_completed":"prompt_failed",{project_id:qe.projectId,model:qe.model,duration_ms:Date.now()-qe.started,tool_calls:qe.calls.size,...X?{}:{error:Ee}}))}function N(X){const he=vc(X.args.request)?X.args.request:{};switch(X.tool){case"write_file":return null;case"read_file":return{tool:"Read",detail:typeof X.args.path=="string"?X.args.path:""};case"search_files":return{tool:"Search",detail:typeof X.args.query=="string"?X.args.query:""};case"edit_file":return null;case"asset_request":return{tool:"Assets",detail:String(X.args.action??"")};case"build":return{tool:"Build",detail:"Scene"};case"view":return{tool:"View",detail:X.args.target==="mobile"?"Mobile preview":X.args.target==="editor"?"Editor":"Current view"};case"playtest":return{tool:"Playtest",detail:`${String(X.args.action??"")}${X.args.platform==="mobile"?" (mobile)":""}`};case"ui_layout":return{tool:"UI layout",detail:"Screen"};case"mobile_check":return{tool:"Mobile check",detail:"HUD and dialogs"};case"scene_request":if(he.type==="batch"&&Array.isArray(he.requests))return{tool:"Scene",detail:`${he.requests.length} changes`};if(he.type==="sceneSettings")return{tool:"Scene",detail:"Settings"};if(he.type==="saveThumbnail")return{tool:"Thumbnail",detail:"New version"};if(he.type==="readThumbnail")return{tool:"Thumbnail",detail:"Current"};if(he.type==="useThumbnail")return{tool:"Thumbnail",detail:`Version ${String(he.version)}`};if(he.type==="attachScript")return null;if(he.type==="createPrefab")return{tool:"Prefab",detail:String(he.from??"")}}return{tool:"Inspect",detail:"Project"}}function L(X,he,Ee,qe){const xt=he.label===void 0?{}:{label:he.label},lt=N(he);if(lt!==null){qe||_({kind:"tool",turnId:X,...lt,...xt});return}if(he.tool==="write_file"||he.tool==="edit_file"){const St=typeof he.args.path=="string"?he.args.path:"";_({kind:"tool",turnId:X,tool:he.tool==="write_file"&&he.args.revision===null?"Write":"Edit",detail:St,...xt})}}async function B(X){const he=l;if(he===null||he.id!==X.turnId||he.controller.signal.aborted)return;let Ee=!1,qe="",xt=!1;try{if(t.wallet()!==he.wallet||t.project().id!==he.projectId)throw new Error("The open project or account changed.");if(X.label!==void 0){const lt=N(X);lt!==null&&(_({kind:"tool",turnId:he.id,...lt,label:X.label}),xt=!0)}if(qe=await he.execute(X,he.controller.signal),qe.length>5e5)throw new Error("The tool result is too large. Ask for a smaller part of the project.");he.controller.signal.throwIfAborted(),Ee=!0,L(he.id,X,qe,xt)}catch(lt){qe=lt instanceof Error?lt.message:typeof lt=="string"&&lt!==""?lt:"The browser tool failed."}l===he&&!he.controller.signal.aborted&&T({kind:"result",turnId:he.id,callId:X.callId,ok:Ee,text:qe})}function G(X){let he;try{he=JSON.parse(X)}catch{A(!1,"Invalid assistant response.","invalid_response");return}if(!vc(he))return;if(he.kind==="failure"&&typeof he.message=="string"){A(!1,he.message,"service_failure");return}if(he.kind==="call"&&typeof he.turnId=="string"&&typeof he.callId=="string"&&CTn.test(he.callId)&&ITn.includes(String(he.tool))&&vc(he.args)){const{label:qe,...xt}=he,lt=typeof qe=="string"?{...xt,label:qe}:xt;if(l===null||l.id!==lt.turnId||l.calls.has(lt.callId))return;l.calls.add(lt.callId);const St=NTn(lt.tool,lt.args),ht=(St?Promise.resolve():f).then(()=>B(lt)).catch(Ue=>{console.error("[assistant] browser tool failed",lt.tool,Ue),(l==null?void 0:l.id)===lt.turnId&&A(!1,"The browser tool failed.","tool_failed")});St||(f=ht);return}if(he.kind!=="event"||!vc(he.event))return;const Ee=he.event;if(!(l===null||Ee.turnId!==l.id)){if(Ee.kind==="text"&&typeof Ee.text=="string"&&_({kind:"text",turnId:l.id,text:Ee.text}),Ee.kind==="error"&&typeof Ee.message=="string"){const qe=Ee.resumable===!0;qe&&(m=r),l.error=qe?"limit":"assistant_error",_({kind:"error",turnId:l.id,message:Ee.message,...qe?{resumable:qe}:{}})}Ee.kind==="done"&&typeof Ee.ok=="boolean"&&A(Ee.ok,void 0,l.error??"assistant_error")}}async function Y(){if((r==null?void 0:r.readyState)===WebSocket.OPEN)return r;if(a!==null)return a;const X=s,he=(async()=>{if(t.service===null)throw new Error("The assistant service is not configured.");const Ee=await t.fetch(`${t.service}/assistant/connect`,{method:"POST"});if(!Ee.ok)throw new Error("The assistant service is unavailable. Sign in and recheck.");const qe=await Ee.json();if(!vc(qe)||typeof qe.token!="string")throw new Error("Invalid assistant connection.");if(X!==s)throw new Error("Connection cancelled.");const xt=new URL(`${t.service}/assistant/socket`,location.href);xt.protocol=xt.protocol==="https:"?"wss:":"ws:";const lt=new WebSocket(xt,[OTn,PTn+qe.token]);return r=lt,new Promise((St,ht)=>{let Ue=!1;const Wt=setTimeout(()=>{lt.close(),ht(new Error("Assistant connection timed out."))},1e4);lt.onmessage=({data:$e})=>{if(r===lt&&typeof $e=="string")if(Ue)G($e);else{let Me;try{Me=JSON.parse($e)}catch{lt.close();return}if(!vc(Me)||Me.kind!=="ready")return;Ue=!0,clearTimeout(Wt),St(lt)}},lt.onerror=()=>{lt.close(),ht(new Error("Could not connect to the assistant service."))},lt.onclose=()=>{clearTimeout(Wt),r===lt&&(r=null,A(!1,"Assistant connection closed. Your edits are saved; the next message starts a new conversation.","connection_closed")),ht(new Error("Assistant connection closed."))}})})();a=he;try{return await he}finally{a===he&&(a=null)}}function Z(){try{return t.project().id}catch{return null}}async function te(X,he){try{const Ee=await t.memory.read(X,he);if(Ee!==null)return Ee;const qe={id:MTr(crypto.getRandomValues(new Uint8Array(16))),key:LTr(crypto.getRandomValues(new Uint8Array(32)))};return await t.memory.write(X,he,qe),qe}catch{return}}function pe(){s++,T({kind:"reset"}),A(!1);const X=r;return r=null,a=null,X==null||X.close(),f}return{shell:{async aiProviders(){if(t.wallet()===null)return x("Sign in to Decentraland to use the assistant.");if(t.service===null)return x("The assistant service is not configured.");try{const X=await t.fetch(`${t.service}/assistant/providers`);if(!X.ok)return x("The assistant service is unavailable. Recheck when it is running.");const he=await X.json();return Array.isArray(he)?he.filter(Ee=>vc(Ee)&&(Ee.id==="claude"||Ee.id==="codex")&&typeof Ee.label=="string"&&typeof Ee.available=="boolean"&&typeof Ee.defaultModel=="string"&&Array.isArray(Ee.models)&&Ee.models.every(qe=>typeof qe=="string")).map(Ee=>({...Ee,transport:"service"})):x("The assistant service returned an invalid provider list.")}catch{return x("Could not reach the assistant service. Recheck your connection.")}},async aiSend(X){var xt,lt;if(l!==null)throw new Error("A turn is already running.");const he=t.wallet();if(he===null)throw new Error("Sign in to use the assistant.");m=null;const Ee=t.project(),qe={id:crypto.randomUUID(),wallet:he,projectId:Ee.id,controller:new AbortController,calls:new Set,execute:DTr(Ee.tools),ended:Ee.tools.turnEnded,started:Date.now(),model:X.model??null,error:null};l=qe,_({kind:"started",turnId:qe.id}),tw("prompt_sent",{project_id:Ee.id,provider:X.provider,model:qe.model,prompt_length:X.text.length,images:((xt=X.images)==null?void 0:xt.length)??0});try{if(await Y(),qe.controller.signal.throwIfAborted(),t.wallet()!==he||t.project().id!==Ee.id)throw new Error("The open project or account changed.");const St=await te(Ee.id,he);qe.controller.signal.throwIfAborted();const ht=(lt=t.mobilePreview)==null?void 0:lt.call(t),Ue=ht===void 0?X.context:[X.context,`[Mobile preview] ${JSON.stringify({mode:ht.mode,ready:ht.ready})}`].filter(Boolean).join(`
34592
34592
 
34593
- `);T({kind:"prompt",turnId:qe.id,projectId:Ee.id,params:{...X,context:Ue},memory:St})}catch(St){l===qe&&A(!1,St instanceof Error?St.message:"Could not start the assistant.","start_failed")}return{turnId:qe.id}},async aiStop(){l!==null&&T({kind:"stop",turnId:l.id}),await pe()},async aiReset(){await pe()},async aiForget(){const X=t.wallet(),he=(l==null?void 0:l.projectId)??Z();l!==null&&T({kind:"stop",turnId:l.id}),A(!1);const Ee=X===null||he===null?null:await t.memory.read(he,X).catch(()=>null);Ee!==null&&X!==null&&he!==null&&(T({kind:"forget",id:Ee.id}),await t.memory.write(he,X,null).catch(()=>{})),await pe()},aiCanResume:()=>m!==null&&m===r&&r.readyState===WebSocket.OPEN,onAiEvent(X){return n.add(X),()=>{n.delete(X)}}},reset:pe,dispose(){pe(),n.clear()}}}function MTr(t){return Array.from(t,n=>n.toString(16).padStart(2,"0")).join("")}function LTr(t){return btoa(String.fromCharCode(...t)).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function TB(t){const n={"scene-compiler/worker.js":"sha384-GCCoSf3nYqyLm3kwF1wvfutA2SwiKz+wOa7yR64wlYmZG3WUWSQDohcoBi9yQy2e","scene-compiler/esbuild.wasm":"sha384-V8Ml7eHSlgGTb/TR1IYB6LtWwAW1NXLDPRjL2FjxwIfqDkSZ/hp+zmfvXDHqu8NV","scene-compiler/sdk-snapshot.json":"sha384-z1EjDWcfwcGr+O+ZEdboDWdpOFpL1uFiAcYiJnz2eZ0tw0QKKfPWZA65ibuYEGAA","three/worker.js":"sha384-6Jh4SOrxzt4ihlgnAMK7VdMudZvVfUv6+8l9qZCjydk8ThjvtCqJprlcuYuMHymg","prefabs/catalog.json":"sha384-I3v1JtW+gcr7Oe6/Fr+nLvr4xKScBHtiGReQXDtBFiiXcFEfOcdmIbDOZD4nVYSK","materials/T_Bark_D.jpg":"sha384-FtUe6G243wHZ8C0kbR+2lFVHp/atYXEPodY4A1qDQquYeu27euNDRDqEyFoQJ29L","materials/T_Bark_N.jpg":"sha384-EhhyT3HTQ3sy1CVvKMTX+ueIGq2+FgDntZ6lKFZW6sHOrCTCMbxnDhthwQIanVOr","materials/T_Cobblestone_D.jpg":"sha384-lSuwYcPhIyqVZvG0NuB93ESZIEqL0ffEmD7gh6iG3qN4elINxt7KQS8CN4Ay2CQ0","materials/T_Cobblestone_N.jpg":"sha384-A+Xe7cTTQGB4hwE6WPsWgD76xwIZ2/FFlxqeWZnyFk5AWaK6hDEKCEZVeVinJ3uw","materials/T_Concrete_D.jpg":"sha384-1dPzQGKxZAw2NloRUM+KhE0X9XeTLUxBrvkw4Uaa4Gm/ID65iDv+yPWUHYi59gYn","materials/T_Concrete_N.jpg":"sha384-8+TiM45B8TCGHYOHh5/510IpXfo1WgyaS5q/T1B3GOEdQrXHV+x9yXmyjkN9mWsh","materials/T_FloorTiles_D.jpg":"sha384-PwDH1P94cY4VXHyrbTQSpvDh89A5jMf+xG62xUDT9TRbItuR6uI1lXB9onxu4gaX","materials/T_FloorTiles_N.jpg":"sha384-opjmq0wL8b+pxHcujr8IGFCOQt8+pDX7EjcnmzmcKGy7F8H3dv9IgG7gxQXgkQJu","materials/T_Grass_D.jpg":"sha384-GGodYBboPnhEcv7JVlR1mSjLWux7q4qJcOxqdl7a/6bN6IWkel4A1BeHV4sieS25","materials/T_Grass_N.jpg":"sha384-zu3ig6Su3u2I3e3LX2zeaS9LtqJYl8V3siVjWBQ2Xea6iMAcMCXHp2HQyctvaEY2","materials/T_Gravel_D.jpg":"sha384-Y2EZTiFeTQbZlduNmz1Y74fZIvkmh8c2grqr6B1GqejChZbNnRb5ejC8SCqyPY77","materials/T_Gravel_N.jpg":"sha384-VWUAPWQZqvuFDmiZZvhAgqQ24DmSrByuu7ETrD2igGikpZS+U7E81hAJ3Iiv88iT","materials/T_Ice_D.jpg":"sha384-qNSYRVYvzZ6NP5Ny/MDMNajmplmSXf9036CidUKqte1DUUDzMm28FZvWcAMSnyux","materials/T_Ice_N.jpg":"sha384-LYz8XTf6DXyxCy6Jtci0y6E0/ER4b73sdH6lhCbagK8nuYPpD8ChpB8PZUTLMLkF","materials/T_Marble_D.jpg":"sha384-MfvwmuTcZs+OB9BLKNAFU849wj27KcdPxxvF3WAY9OrZGtQORoHXjDWh2YWBukWk","materials/T_Marble_N.jpg":"sha384-SU6JhDtveHoi5d1AUiQV8oVfO1uEChNA7ACvMv1OIKESECfG1lCMw6MG7TZrGhnf","materials/T_MetalPlate_D.jpg":"sha384-Qe4yIHUfUVMwOEDZTsY0u7d19xhUE/GiSyz0s3P9297mx+XXGvDCR8gKdU9hLhbo","materials/T_MetalPlate_N.jpg":"sha384-RpCdFzgg7xIUz1G+nDJEon1H0MHwsk4RHOTfi2Er2hC56AXU2TXcToTY12aqz+4R","materials/T_Metal_D.jpg":"sha384-h2Fs2s1/WPYJwUY3a6vpMqrd0rb7GDWViOvw8SlsSocO/sBqcqzzbjOwYR9uooVE","materials/T_Metal_N.jpg":"sha384-iVL98sXzJCK2z/YuoBw0pwbZyes0ZprTHsvQdFQ6tXhKP5004tHMWJnr5icUVwKp","materials/T_MossyRock_D.jpg":"sha384-5sB1xTHnLgeGIiY5kqc5kr78xBvR17BBrWJZD1IyyEuFO8mcgJ7DaAKH9ZcrV0Oj","materials/T_MossyRock_N.jpg":"sha384-z+02Z5HZoLwVk/i0R/YRm/MW/KQr3Y5h4cB0iH837QsKMrvzrO6iFMuBWXER8ZLk","materials/T_OldBrick_D.jpg":"sha384-1h+VtTgiyyfZ9N5h15snvPP5bjvvI5Mzq958DaMi0BTJKxFbtKoYjnWp9mkhDBTZ","materials/T_OldBrick_N.jpg":"sha384-mORhqcYtb2adL11dYPIpTKvrTVqiz1mqHVW/abZaSerjsKMEvRa5uxoe65nfUmzF","materials/T_Planks_D.jpg":"sha384-olCbQDlaUR5rNCiEPsJt3Z6XYD2L1McfZNZOBdCDsIo4dyKa9Y41H3mCbdY1BMqL","materials/T_Planks_N.jpg":"sha384-Qe2LOHGe2f/b8RdZmZRSrZl0KMgxMT28Q0yj/Ewr0TRsmSe4L1N9TxQscZ7jXIpz","materials/T_RedBrick_D.jpg":"sha384-z5Omhin/SbXfu6KqUuKYyVFQN5GIkfCtva/SIM3g8C09Dy/ajUgaCrQYUQv0Wi6s","materials/T_RedBrick_N.jpg":"sha384-N5Zlk1HLC7ECYSYp9JAlKc9UZk3lFIytrdyYTNCyrRAD6IMQMGM178Ns5ICXoBcY","materials/T_Rock_D.jpg":"sha384-JPbUh7M8Yt82J9u9P3cMPWNBNNDN2MOkMhb4BpWOQ1MUacVygOKoKHOAspiyAaDI","materials/T_Rock_N.jpg":"sha384-d2AXOT8FPWfSBdhZGN2U85WRbKq02qgDIFhEcp9/ZrTfzPPpb3x+1tM3pRfflGWG","materials/T_RoofTiles_D.jpg":"sha384-XyFas+mKIHi1ZJxhOpjcJ3aE7SZjOUukcY1wZqJ0kcwMjYE2SD5mv/Ft1babZiRk","materials/T_RoofTiles_N.jpg":"sha384-OtknIACV+BRu+g5+hVLWILG4Qli4fIDdqqrf3rFTuGr/VxhZIGgxC61tMM7Tr5pt","materials/T_RustyMetal_D.jpg":"sha384-vN18ZCLm3ijlv9l30lI/rUbcUpcJH0ut4hF8GueDNYPZO4WDiqxJH9EX70DGsEsh","materials/T_RustyMetal_N.jpg":"sha384-+rdn3/f1t/jU65sT0kq03mG9HtAIA3a1+bezGQmXOV9FiCM2KUCIugsOUhQAzmjO","materials/T_Sand_D.jpg":"sha384-i3zMj/gXeIpnlll08lxRRvQ9BA+j/3bLnzru+M3LzjvyhwMKgz6bmnLjhe1rPZrz","materials/T_Sand_N.jpg":"sha384-eBZtZpK8wZnUmtNfV5iCt/l4WWvoppG0bFkFeULvqw5gNkT+emO5u8AVtpRTFjxM","materials/T_Snow_D.jpg":"sha384-kOAg+1SQpFtFg/XpbSTT+Boo6hGe/+BXR3fI2pgrTyl0VJTrc2W1pUKlT38nSP6k","materials/T_Snow_N.jpg":"sha384-8ryXPS0Yprm5vNZCUadJRhKh4MQ+8gkD0q1GbjlPLez7GSjsV7QmAyM63PiIn0cK","materials/T_StoneBlocks_D.jpg":"sha384-o2TlIP/+m+uvEmypAk3zXE+9huFaU3K+VTD18p8vPEb9I3SVB1n2ez1jqZTXZNQF","materials/T_StoneBlocks_N.jpg":"sha384-eR/UR9FC3v3Pw4oBlWlOyQNmtgvb5a1mSRwSBMrQhDyXk4neOjmVlTyjBjhOTYOU","materials/T_Wood_D.jpg":"sha384-Tk9jMT15vysCDRdd22J2mFUHCBSHVMhGNa71sYFf7yqvZQkmMGdrIhiIsmi/fAm2","materials/T_Wood_N.jpg":"sha384-b1SWrWCwyf+89JAlVNPCwmwkUTrcGXUqRANI3x+grecaCQblZGLsSaJx/5CNPnPC","sounds/click.mp3":"sha384-5Nn2A2LWwXVAhOJKiwTncPtBTCtmrqOVQVAuz+xHRrAuMnScsTQ06gM0dU14yxa1","sounds/close.mp3":"sha384-JBoxn91EXhKXI4EJZ1nlDO3U735Nz6Nn1UuzpPhRjjpMaEOafABEqQL8GgfV7XTi","sounds/coin.mp3":"sha384-2RR7dy+f4rzUKdiGeSdApfQmbsENC4FzT8o4y3JXs5IX6a2udQbwU3iLhCUZ+z1a","sounds/countdown-tick.mp3":"sha384-I1gWiwXYWkrQ3dmkVDqolRCU+O/OkVqH625gdQZ8i3mntGSNBKJN1u2yPzmiPOio","sounds/door-close.mp3":"sha384-V/WOG/jNZMVo2EK2wosamkz0F/OjilfprwqVR0JwxFuLjzaIGOG6LFUxp1RB2NF6","sounds/door-open.mp3":"sha384-toZQ6Li9ckUBKJ2Ct9j0VihbOL/1cckuyURTQZoeGWNs9O37cudKQDiQZwGpY7zq","sounds/error.mp3":"sha384-/1dEHJ1qoAxzjSucYUOOSu2v1iZYjiVyKIKHvXI6YgbdXR9ekFvFcoeyGSvpOClG","sounds/explosion.mp3":"sha384-ltgcuLllD+21WOWShc9fHRcMCHja6CjvrriwZ6q+svIzA6sjbOzL7as5pD7F6cZg","sounds/fire-loop.mp3":"sha384-961Bvf7i+Y4Ij2823D68797moLbDjrru/1gSbAn3s9G5acVoQbcwrE1hqd94K/XV","sounds/gem.mp3":"sha384-R3zEhJWNA8xL6l1SGy4l81c6sRAsYweKJr1LzwbB/te+hd1PRu6vtm+z1pV8JHV/","sounds/hit.mp3":"sha384-gNRf3+uQRBdPYgenEiKF7x4nZTu0+MSc3k6g4Mzj9pERNfyzmB0eluHrvUSXIQxf","sounds/jump.mp3":"sha384-5Fq5FzFq3J7aaa65LLaRIQ2rDWdBrj6kX1YuIxizC+N5DKmz6yVrZvhOEcdHETZa","sounds/land.mp3":"sha384-FzP8V/7gNObRoNTgDtomIrCIaLPQwp45q2AoqEEmxTVHzgf2Tqs56rqNcasWD/Df","sounds/lever.mp3":"sha384-/l81+1qFlkY+VOiv9vSv80lS2V6FUoHnNNw1CHWNHkxaWbE7qXXIn5byS/0tNnwQ","sounds/lose.mp3":"sha384-Fko/fFGTQf6WOcJz9iDlgfYtldodfjcUgH0hgNGU72c+OntJLlAYQ0i8ZcRFBuoA","sounds/machine-loop.mp3":"sha384-HK+6s/ohv0LmgMNH4XCP8gYdyYNsUQ7MiBgCJSvnznGhXaviNkvZHMZZTo2+Nvkl","sounds/open.mp3":"sha384-r8PUU3VxFOwpVcmIhwYOg6BYUhYrOQnJT4CgVz4V9ooVpM0NeaAhGMPURavBdanE","sounds/point.mp3":"sha384-98vZ+QorY3iw1bm7sRNIm54Mq0LroKWsCNFrPt6aAQ3ZhHN7+I4ibwsu+TxgWf3a","sounds/power-up.mp3":"sha384-URbVyheAkb87lZdDT3iWJQAIJfyUzk8ieEwX2Hv/PFNSqnO098E9mqdhy0KeQM9m","sounds/rumble-loop.mp3":"sha384-1KsyvSyAN9bm4ZrCRF/3+bHDDVvnC560HXk48Zjr+26capvQS0IvtmvFqrdcFgs/","sounds/splat.mp3":"sha384-2aZ/C6hYBRqXPnSZq2TB9M5u3EFDOVxQjeMGOswRv43CzuL0ZAysUhX/nnHr07Hi","sounds/start.mp3":"sha384-zw5mXd2OL6pbHLz5xdyjQn9nc7AVDgLqx1MSiPYehc6yI0Odt5qTh1TyJNpqxJVC","sounds/teleport.mp3":"sha384-pkpm8x8Aknup+URiIAhz9whdPkPLRoJlhdGYRM/2FJyzzaemjIZoIveJ339G3eFU","sounds/time-up.mp3":"sha384-UeQg6KkMKoeiiRIft0T5z3MU8NM9hLZbx3Smhsz7voAqGb/u7Ndag6lpHOaxqhVY","sounds/win.mp3":"sha384-I8Pyt8U4kbyk9xAoxcimTKSgF9BTr4Nf1t63E/Olnahu2FKJ0G6oLPIVD7CotGiH","editor.css":"sha384-UtVv2Sy+fO5029No4SyC1iW7hUXbIfy4nqF1QP2S8A7M1rUOv5qoJPnxdiNzAF60"}[t];if(n===void 0)throw new Error(`${t} is not one of the editor's published files`);return n}function FTr(t){const n=r=>new URL(`scene-compiler/${r}`,t).href;return{workerUrl:n("worker.js"),wasmUrl:n("esbuild.wasm"),snapshotUrl:n("sdk-snapshot.json"),integrity:{worker:TB("scene-compiler/worker.js"),wasm:TB("scene-compiler/esbuild.wasm"),snapshot:TB("scene-compiler/sdk-snapshot.json")}}}function jTr(t){let n=null;return async()=>(n??(n=LTe(new URL("three/worker.js",t).href,TB("three/worker.js"),"three/worker.js").then(r=>r.text())),n.catch(()=>{n=null}),await n)}let rqe=null;function BTr(t){rqe=t}async function cqt(t){if(rqe===null)throw new Error("The shared library is not available here.");const n=await LTe(new URL(t,rqe).href,TB(t),t);return new Uint8Array(await n.arrayBuffer())}const UTr=(t,n)=>cqt(qXe(t,n)),$Tr=t=>cqt(Fzt(t)),iqe={".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".gif":"image/gif",".webp":"image/webp"};function N0(t){if(t===void 0)return null;try{const n=JSON.parse(new TextDecoder().decode(t));return typeof n=="object"&&n!==null&&!Array.isArray(n)?n:null}catch{return null}}const VOt=t=>new TextEncoder().encode(JSON.stringify(t,null,2)+`
34593
+ `);T({kind:"prompt",turnId:qe.id,projectId:Ee.id,params:{...X,context:Ue},memory:St})}catch(St){l===qe&&A(!1,St instanceof Error?St.message:"Could not start the assistant.","start_failed")}return{turnId:qe.id}},async aiStop(){l!==null&&T({kind:"stop",turnId:l.id}),await pe()},async aiReset(){await pe()},async aiForget(){const X=t.wallet(),he=(l==null?void 0:l.projectId)??Z();l!==null&&T({kind:"stop",turnId:l.id}),A(!1);const Ee=X===null||he===null?null:await t.memory.read(he,X).catch(()=>null);Ee!==null&&X!==null&&he!==null&&(T({kind:"forget",id:Ee.id}),await t.memory.write(he,X,null).catch(()=>{})),await pe()},aiCanResume:()=>m!==null&&m===r&&r.readyState===WebSocket.OPEN,onAiEvent(X){return n.add(X),()=>{n.delete(X)}}},reset:pe,dispose(){pe(),n.clear()}}}function MTr(t){return Array.from(t,n=>n.toString(16).padStart(2,"0")).join("")}function LTr(t){return btoa(String.fromCharCode(...t)).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function TB(t){const n={"scene-compiler/worker.js":"sha384-GCCoSf3nYqyLm3kwF1wvfutA2SwiKz+wOa7yR64wlYmZG3WUWSQDohcoBi9yQy2e","scene-compiler/esbuild.wasm":"sha384-V8Ml7eHSlgGTb/TR1IYB6LtWwAW1NXLDPRjL2FjxwIfqDkSZ/hp+zmfvXDHqu8NV","scene-compiler/sdk-snapshot.json":"sha384-z1EjDWcfwcGr+O+ZEdboDWdpOFpL1uFiAcYiJnz2eZ0tw0QKKfPWZA65ibuYEGAA","three/worker.js":"sha384-6Jh4SOrxzt4ihlgnAMK7VdMudZvVfUv6+8l9qZCjydk8ThjvtCqJprlcuYuMHymg","prefabs/catalog.json":"sha384-WqAV9BbIp/wkIX++5MRREzNZN6UOJd1x/rd/1zC50376aferFpiI1jbmYJD6O+qQ","materials/T_Bark_D.jpg":"sha384-FtUe6G243wHZ8C0kbR+2lFVHp/atYXEPodY4A1qDQquYeu27euNDRDqEyFoQJ29L","materials/T_Bark_N.jpg":"sha384-EhhyT3HTQ3sy1CVvKMTX+ueIGq2+FgDntZ6lKFZW6sHOrCTCMbxnDhthwQIanVOr","materials/T_Cobblestone_D.jpg":"sha384-lSuwYcPhIyqVZvG0NuB93ESZIEqL0ffEmD7gh6iG3qN4elINxt7KQS8CN4Ay2CQ0","materials/T_Cobblestone_N.jpg":"sha384-A+Xe7cTTQGB4hwE6WPsWgD76xwIZ2/FFlxqeWZnyFk5AWaK6hDEKCEZVeVinJ3uw","materials/T_Concrete_D.jpg":"sha384-1dPzQGKxZAw2NloRUM+KhE0X9XeTLUxBrvkw4Uaa4Gm/ID65iDv+yPWUHYi59gYn","materials/T_Concrete_N.jpg":"sha384-8+TiM45B8TCGHYOHh5/510IpXfo1WgyaS5q/T1B3GOEdQrXHV+x9yXmyjkN9mWsh","materials/T_FloorTiles_D.jpg":"sha384-PwDH1P94cY4VXHyrbTQSpvDh89A5jMf+xG62xUDT9TRbItuR6uI1lXB9onxu4gaX","materials/T_FloorTiles_N.jpg":"sha384-opjmq0wL8b+pxHcujr8IGFCOQt8+pDX7EjcnmzmcKGy7F8H3dv9IgG7gxQXgkQJu","materials/T_Grass_D.jpg":"sha384-GGodYBboPnhEcv7JVlR1mSjLWux7q4qJcOxqdl7a/6bN6IWkel4A1BeHV4sieS25","materials/T_Grass_N.jpg":"sha384-zu3ig6Su3u2I3e3LX2zeaS9LtqJYl8V3siVjWBQ2Xea6iMAcMCXHp2HQyctvaEY2","materials/T_Gravel_D.jpg":"sha384-Y2EZTiFeTQbZlduNmz1Y74fZIvkmh8c2grqr6B1GqejChZbNnRb5ejC8SCqyPY77","materials/T_Gravel_N.jpg":"sha384-VWUAPWQZqvuFDmiZZvhAgqQ24DmSrByuu7ETrD2igGikpZS+U7E81hAJ3Iiv88iT","materials/T_Ice_D.jpg":"sha384-qNSYRVYvzZ6NP5Ny/MDMNajmplmSXf9036CidUKqte1DUUDzMm28FZvWcAMSnyux","materials/T_Ice_N.jpg":"sha384-LYz8XTf6DXyxCy6Jtci0y6E0/ER4b73sdH6lhCbagK8nuYPpD8ChpB8PZUTLMLkF","materials/T_Marble_D.jpg":"sha384-MfvwmuTcZs+OB9BLKNAFU849wj27KcdPxxvF3WAY9OrZGtQORoHXjDWh2YWBukWk","materials/T_Marble_N.jpg":"sha384-SU6JhDtveHoi5d1AUiQV8oVfO1uEChNA7ACvMv1OIKESECfG1lCMw6MG7TZrGhnf","materials/T_MetalPlate_D.jpg":"sha384-Qe4yIHUfUVMwOEDZTsY0u7d19xhUE/GiSyz0s3P9297mx+XXGvDCR8gKdU9hLhbo","materials/T_MetalPlate_N.jpg":"sha384-RpCdFzgg7xIUz1G+nDJEon1H0MHwsk4RHOTfi2Er2hC56AXU2TXcToTY12aqz+4R","materials/T_Metal_D.jpg":"sha384-h2Fs2s1/WPYJwUY3a6vpMqrd0rb7GDWViOvw8SlsSocO/sBqcqzzbjOwYR9uooVE","materials/T_Metal_N.jpg":"sha384-iVL98sXzJCK2z/YuoBw0pwbZyes0ZprTHsvQdFQ6tXhKP5004tHMWJnr5icUVwKp","materials/T_MossyRock_D.jpg":"sha384-5sB1xTHnLgeGIiY5kqc5kr78xBvR17BBrWJZD1IyyEuFO8mcgJ7DaAKH9ZcrV0Oj","materials/T_MossyRock_N.jpg":"sha384-z+02Z5HZoLwVk/i0R/YRm/MW/KQr3Y5h4cB0iH837QsKMrvzrO6iFMuBWXER8ZLk","materials/T_OldBrick_D.jpg":"sha384-1h+VtTgiyyfZ9N5h15snvPP5bjvvI5Mzq958DaMi0BTJKxFbtKoYjnWp9mkhDBTZ","materials/T_OldBrick_N.jpg":"sha384-mORhqcYtb2adL11dYPIpTKvrTVqiz1mqHVW/abZaSerjsKMEvRa5uxoe65nfUmzF","materials/T_Planks_D.jpg":"sha384-olCbQDlaUR5rNCiEPsJt3Z6XYD2L1McfZNZOBdCDsIo4dyKa9Y41H3mCbdY1BMqL","materials/T_Planks_N.jpg":"sha384-Qe2LOHGe2f/b8RdZmZRSrZl0KMgxMT28Q0yj/Ewr0TRsmSe4L1N9TxQscZ7jXIpz","materials/T_RedBrick_D.jpg":"sha384-z5Omhin/SbXfu6KqUuKYyVFQN5GIkfCtva/SIM3g8C09Dy/ajUgaCrQYUQv0Wi6s","materials/T_RedBrick_N.jpg":"sha384-N5Zlk1HLC7ECYSYp9JAlKc9UZk3lFIytrdyYTNCyrRAD6IMQMGM178Ns5ICXoBcY","materials/T_Rock_D.jpg":"sha384-JPbUh7M8Yt82J9u9P3cMPWNBNNDN2MOkMhb4BpWOQ1MUacVygOKoKHOAspiyAaDI","materials/T_Rock_N.jpg":"sha384-d2AXOT8FPWfSBdhZGN2U85WRbKq02qgDIFhEcp9/ZrTfzPPpb3x+1tM3pRfflGWG","materials/T_RoofTiles_D.jpg":"sha384-XyFas+mKIHi1ZJxhOpjcJ3aE7SZjOUukcY1wZqJ0kcwMjYE2SD5mv/Ft1babZiRk","materials/T_RoofTiles_N.jpg":"sha384-OtknIACV+BRu+g5+hVLWILG4Qli4fIDdqqrf3rFTuGr/VxhZIGgxC61tMM7Tr5pt","materials/T_RustyMetal_D.jpg":"sha384-vN18ZCLm3ijlv9l30lI/rUbcUpcJH0ut4hF8GueDNYPZO4WDiqxJH9EX70DGsEsh","materials/T_RustyMetal_N.jpg":"sha384-+rdn3/f1t/jU65sT0kq03mG9HtAIA3a1+bezGQmXOV9FiCM2KUCIugsOUhQAzmjO","materials/T_Sand_D.jpg":"sha384-i3zMj/gXeIpnlll08lxRRvQ9BA+j/3bLnzru+M3LzjvyhwMKgz6bmnLjhe1rPZrz","materials/T_Sand_N.jpg":"sha384-eBZtZpK8wZnUmtNfV5iCt/l4WWvoppG0bFkFeULvqw5gNkT+emO5u8AVtpRTFjxM","materials/T_Snow_D.jpg":"sha384-kOAg+1SQpFtFg/XpbSTT+Boo6hGe/+BXR3fI2pgrTyl0VJTrc2W1pUKlT38nSP6k","materials/T_Snow_N.jpg":"sha384-8ryXPS0Yprm5vNZCUadJRhKh4MQ+8gkD0q1GbjlPLez7GSjsV7QmAyM63PiIn0cK","materials/T_StoneBlocks_D.jpg":"sha384-o2TlIP/+m+uvEmypAk3zXE+9huFaU3K+VTD18p8vPEb9I3SVB1n2ez1jqZTXZNQF","materials/T_StoneBlocks_N.jpg":"sha384-eR/UR9FC3v3Pw4oBlWlOyQNmtgvb5a1mSRwSBMrQhDyXk4neOjmVlTyjBjhOTYOU","materials/T_Wood_D.jpg":"sha384-Tk9jMT15vysCDRdd22J2mFUHCBSHVMhGNa71sYFf7yqvZQkmMGdrIhiIsmi/fAm2","materials/T_Wood_N.jpg":"sha384-b1SWrWCwyf+89JAlVNPCwmwkUTrcGXUqRANI3x+grecaCQblZGLsSaJx/5CNPnPC","sounds/click.mp3":"sha384-5Nn2A2LWwXVAhOJKiwTncPtBTCtmrqOVQVAuz+xHRrAuMnScsTQ06gM0dU14yxa1","sounds/close.mp3":"sha384-JBoxn91EXhKXI4EJZ1nlDO3U735Nz6Nn1UuzpPhRjjpMaEOafABEqQL8GgfV7XTi","sounds/coin.mp3":"sha384-2RR7dy+f4rzUKdiGeSdApfQmbsENC4FzT8o4y3JXs5IX6a2udQbwU3iLhCUZ+z1a","sounds/countdown-tick.mp3":"sha384-I1gWiwXYWkrQ3dmkVDqolRCU+O/OkVqH625gdQZ8i3mntGSNBKJN1u2yPzmiPOio","sounds/door-close.mp3":"sha384-V/WOG/jNZMVo2EK2wosamkz0F/OjilfprwqVR0JwxFuLjzaIGOG6LFUxp1RB2NF6","sounds/door-open.mp3":"sha384-toZQ6Li9ckUBKJ2Ct9j0VihbOL/1cckuyURTQZoeGWNs9O37cudKQDiQZwGpY7zq","sounds/error.mp3":"sha384-/1dEHJ1qoAxzjSucYUOOSu2v1iZYjiVyKIKHvXI6YgbdXR9ekFvFcoeyGSvpOClG","sounds/explosion.mp3":"sha384-ltgcuLllD+21WOWShc9fHRcMCHja6CjvrriwZ6q+svIzA6sjbOzL7as5pD7F6cZg","sounds/fire-loop.mp3":"sha384-961Bvf7i+Y4Ij2823D68797moLbDjrru/1gSbAn3s9G5acVoQbcwrE1hqd94K/XV","sounds/gem.mp3":"sha384-R3zEhJWNA8xL6l1SGy4l81c6sRAsYweKJr1LzwbB/te+hd1PRu6vtm+z1pV8JHV/","sounds/hit.mp3":"sha384-gNRf3+uQRBdPYgenEiKF7x4nZTu0+MSc3k6g4Mzj9pERNfyzmB0eluHrvUSXIQxf","sounds/jump.mp3":"sha384-5Fq5FzFq3J7aaa65LLaRIQ2rDWdBrj6kX1YuIxizC+N5DKmz6yVrZvhOEcdHETZa","sounds/land.mp3":"sha384-FzP8V/7gNObRoNTgDtomIrCIaLPQwp45q2AoqEEmxTVHzgf2Tqs56rqNcasWD/Df","sounds/lever.mp3":"sha384-/l81+1qFlkY+VOiv9vSv80lS2V6FUoHnNNw1CHWNHkxaWbE7qXXIn5byS/0tNnwQ","sounds/lose.mp3":"sha384-Fko/fFGTQf6WOcJz9iDlgfYtldodfjcUgH0hgNGU72c+OntJLlAYQ0i8ZcRFBuoA","sounds/machine-loop.mp3":"sha384-HK+6s/ohv0LmgMNH4XCP8gYdyYNsUQ7MiBgCJSvnznGhXaviNkvZHMZZTo2+Nvkl","sounds/open.mp3":"sha384-r8PUU3VxFOwpVcmIhwYOg6BYUhYrOQnJT4CgVz4V9ooVpM0NeaAhGMPURavBdanE","sounds/point.mp3":"sha384-98vZ+QorY3iw1bm7sRNIm54Mq0LroKWsCNFrPt6aAQ3ZhHN7+I4ibwsu+TxgWf3a","sounds/power-up.mp3":"sha384-URbVyheAkb87lZdDT3iWJQAIJfyUzk8ieEwX2Hv/PFNSqnO098E9mqdhy0KeQM9m","sounds/rumble-loop.mp3":"sha384-1KsyvSyAN9bm4ZrCRF/3+bHDDVvnC560HXk48Zjr+26capvQS0IvtmvFqrdcFgs/","sounds/splat.mp3":"sha384-2aZ/C6hYBRqXPnSZq2TB9M5u3EFDOVxQjeMGOswRv43CzuL0ZAysUhX/nnHr07Hi","sounds/start.mp3":"sha384-zw5mXd2OL6pbHLz5xdyjQn9nc7AVDgLqx1MSiPYehc6yI0Odt5qTh1TyJNpqxJVC","sounds/teleport.mp3":"sha384-pkpm8x8Aknup+URiIAhz9whdPkPLRoJlhdGYRM/2FJyzzaemjIZoIveJ339G3eFU","sounds/time-up.mp3":"sha384-UeQg6KkMKoeiiRIft0T5z3MU8NM9hLZbx3Smhsz7voAqGb/u7Ndag6lpHOaxqhVY","sounds/win.mp3":"sha384-I8Pyt8U4kbyk9xAoxcimTKSgF9BTr4Nf1t63E/Olnahu2FKJ0G6oLPIVD7CotGiH","editor.css":"sha384-UtVv2Sy+fO5029No4SyC1iW7hUXbIfy4nqF1QP2S8A7M1rUOv5qoJPnxdiNzAF60"}[t];if(n===void 0)throw new Error(`${t} is not one of the editor's published files`);return n}function FTr(t){const n=r=>new URL(`scene-compiler/${r}`,t).href;return{workerUrl:n("worker.js"),wasmUrl:n("esbuild.wasm"),snapshotUrl:n("sdk-snapshot.json"),integrity:{worker:TB("scene-compiler/worker.js"),wasm:TB("scene-compiler/esbuild.wasm"),snapshot:TB("scene-compiler/sdk-snapshot.json")}}}function jTr(t){let n=null;return async()=>(n??(n=LTe(new URL("three/worker.js",t).href,TB("three/worker.js"),"three/worker.js").then(r=>r.text())),n.catch(()=>{n=null}),await n)}let rqe=null;function BTr(t){rqe=t}async function cqt(t){if(rqe===null)throw new Error("The shared library is not available here.");const n=await LTe(new URL(t,rqe).href,TB(t),t);return new Uint8Array(await n.arrayBuffer())}const UTr=(t,n)=>cqt(qXe(t,n)),$Tr=t=>cqt(Fzt(t)),iqe={".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".gif":"image/gif",".webp":"image/webp"};function N0(t){if(t===void 0)return null;try{const n=JSON.parse(new TextDecoder().decode(t));return typeof n=="object"&&n!==null&&!Array.isArray(n)?n:null}catch{return null}}const VOt=t=>new TextEncoder().encode(JSON.stringify(t,null,2)+`
34594
34594
  `);function lqt(t){const n=t.lastIndexOf(".");return n===-1?"":t.slice(n).toLowerCase()}function WOt(t,n){let r="";for(let a=0;a<n.length;a+=32768)r+=String.fromCharCode(...n.subarray(a,a+32768));return`data:${iqe[lqt(t)]??"image/png"};base64,${btoa(r)}`}function AR(t){return Array.isArray(t)?t.length===0?0:(Math.min(...t)+Math.max(...t))/2:typeof t=="number"?t:0}function zTr(t){var n,r,a,s,l,f,m;return{title:((n=t.display)==null?void 0:n.title)??"",description:((r=t.display)==null?void 0:r.description)??"",thumbnailPath:((a=t.display)==null?void 0:a.navmapThumbnail)??null,contactName:((s=t.contact)==null?void 0:s.name)??"",contactEmail:((l=t.contact)==null?void 0:l.email)??"",parcels:Array.isArray((f=t.scene)==null?void 0:f.parcels)?[...t.scene.parcels]:[],base:((m=t.scene)==null?void 0:m.base)??"",spawnPoints:(t.spawnPoints??[]).map((_,x)=>{var T,A,N;return{name:_.name??`Spawn Point ${x+1}`,default:_.default===!0,position:{x:AR((T=_.position)==null?void 0:T.x),y:AR((A=_.position)==null?void 0:A.y),z:AR((N=_.position)==null?void 0:N.z)},..._.cameraTarget!==void 0?{cameraTarget:{x:AR(_.cameraTarget.x),y:AR(_.cameraTarget.y),z:AR(_.cameraTarget.z)}}:{}}})}}const uqt=/^-?\d+,-?\d+$/;function VTr(t){if(t.title.trim()==="")return"The scene needs a name";if(t.parcels.length===0)return"The scene needs at least one parcel";const n=t.parcels.find(r=>!uqt.test(r));if(n!==void 0)return`"${n}" is not a valid parcel — use "x,y"`;if(new Set(t.parcels).size!==t.parcels.length)return"Duplicate parcels";if(!t.parcels.includes(t.base))return"The base parcel must be one of the scene’s parcels";for(const r of t.spawnPoints){if(r.name.trim()==="")return"Every spawn point needs a name";for(const a of[r.position.x,r.position.y,r.position.z])if(!Number.isFinite(a))return`Spawn point "${r.name}" has an invalid position`;if(r.cameraTarget!==void 0){for(const a of[r.cameraTarget.x,r.cameraTarget.y,r.cameraTarget.z])if(!Number.isFinite(a))return`Spawn point "${r.name}" has an invalid camera target`}}return null}function WTr(t,n){const r=n.spawnPoints.map(a=>({name:a.name.trim(),default:a.default,position:{x:a.position.x,y:a.position.y,z:a.position.z},...a.cameraTarget!==void 0?{cameraTarget:{...a.cameraTarget}}:{}}));return{...t,display:{...t.display??{},title:n.title.trim(),description:n.description.trim(),...n.thumbnailPath!==null?{navmapThumbnail:n.thumbnailPath}:{}},contact:{...t.contact??{},name:n.contactName.trim(),email:n.contactEmail.trim()},scene:{...t.scene??{},parcels:[...n.parcels],base:n.base},spawnPoints:r}}function qTr(t){var m,_,x;const[n,r]=(((m=t.scene)==null?void 0:m.base)??"0,0").split(",").map(T=>parseInt(T,10));if(!Number.isFinite(n)||!Number.isFinite(r))return"";const a=((_=t.spawnPoints)==null?void 0:_.find(T=>T.default))??((x=t.spawnPoints)==null?void 0:x[0]);if((a==null?void 0:a.position)===void 0)return`${n*16+8},0,${r*16+8}`;const{x:s,y:l,z:f}=a.position;return`${n*16+AR(s)},${AR(l)},${r*16+AR(f)}`}const GTr=["assets/scene/main.composite","main.composite","assets/main.composite"];function HTr(t){let n;try{n=JSON.parse(t)}catch{return[]}const r=new Set;for(const a of n.components??[])if((a==null?void 0:a.name)!=="composite::root")for(const s of Object.keys((a==null?void 0:a.data)??{})){const l=Number(s);Number.isFinite(l)&&l>0&&r.add(l)}return[...r].sort((a,s)=>a-s)}const qOt=["ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE","ALLOW_TO_TRIGGER_AVATAR_EMOTE","OPEN_EXTERNAL_LINK","USE_WEB3_API","USE_FETCH","USE_WEBSOCKET"],GOt=100;function JTr(t){const n=new Set(t),r=new Set([t[0]]),a=[t[0]];for(;a.length>0;){const[s,l]=a.pop().split(",").map(Number);for(const f of[`${s+1},${l}`,`${s-1},${l}`,`${s},${l+1}`,`${s},${l-1}`])n.has(f)&&!r.has(f)&&(r.add(f),a.push(f))}return r.size===n.size}function HOt(t){if(typeof t!="object"||t===null)return null;const{x:n,y:r,z:a}=t;return[n,r,a].every(s=>typeof s=="number"&&Number.isFinite(s))?{x:n,y:r,z:a}:null}function QTr(t,n){const r={...t};if(n.thumbnailPath!==void 0){if(typeof n.thumbnailPath!="string"||!/^assets\/images\/[\w-]+\.(jpg|png)$/.test(n.thumbnailPath))throw new Error("thumbnailPath must be an image under assets/images/");r.display={...t.display??{},navmapThumbnail:n.thumbnailPath}}if(n.permissions!==void 0){if(!Array.isArray(n.permissions)||n.permissions.some(s=>typeof s!="string"||!qOt.includes(s)))throw new Error(`permissions must be some of: ${qOt.join(", ")}`);const a=Array.isArray(t.requiredPermissions)?t.requiredPermissions.filter(s=>typeof s=="string"):[];r.requiredPermissions=[...new Set([...a,...n.permissions])]}if(n.parcels!==void 0||n.base!==void 0){const a=t.scene??{},s=n.parcels??a.parcels;if(!Array.isArray(s)||s.length===0||s.length>GOt||s.some(m=>typeof m!="string"||!uqt.test(m)))throw new Error(`parcels must be a list of 1–${GOt} "x,y" coordinates`);const l=s.map(m=>m.split(",").map(Number).join(","));if(new Set(l).size!==l.length)throw new Error("parcels has duplicates");if(!JTr(l))throw new Error("parcels must form one connected area: each one shares an edge with another");const f=n.base??(typeof a.base=="string"&&l.includes(a.base)?a.base:l[0]);if(typeof f!="string"||!l.includes(f))throw new Error("base must be one of the parcels");r.scene={...a,parcels:l,base:f}}if(n.spawnPoints!==void 0){if(!Array.isArray(n.spawnPoints)||n.spawnPoints.length>16)throw new Error("spawnPoints must be a list of up to 16 points");r.spawnPoints=n.spawnPoints.map((a,s)=>{const l=typeof a=="object"&&a!==null?a:{},f=HOt(l.position);if(f===null)throw new Error(`spawn point ${s+1} needs a position {x,y,z} in scene metres`);const m=l.cameraTarget===void 0?void 0:HOt(l.cameraTarget);if(m===null)throw new Error(`spawn point ${s+1} has an invalid cameraTarget`);return{name:typeof l.name=="string"&&l.name.trim()!==""?l.name.trim().slice(0,100):`Spawn Point ${s+1}`,default:l.default===!0||s===0&&!n.spawnPoints.some(_=>(_==null?void 0:_.default)===!0),position:f,...m!==void 0?{cameraTarget:m}:{}}})}return r}const YTr=new Set(["@dcl/sdk","@dcl/ecs","@dcl/ecs-math","@dcl/js-runtime","@dcl/react-ecs","@dcl/asset-packs","react","react-reconciler","scheduler","protobufjs","long","text-encoding"]),XTr=/^(@dcl\/sdk-commands|typescript|eslint|prettier)$|^@types\/|^eslint-|^@typescript-eslint\//;function dqt(t){const n=N0(t),r={};for(const a of[n==null?void 0:n.dependencies,n==null?void 0:n.devDependencies])if(!(typeof a!="object"||a===null))for(const[s,l]of Object.entries(a))typeof l=="string"&&!YTr.has(s)&&!XTr.test(s)&&(r[s]=l);return r}class Wg extends Error{constructor(n,r,a=!1,s){super(r),this.reason=n,this.retryable=a,this.detail=s,this.name="PublishError"}}const JOt=t=>{const n=t/1048576;return`${n>=10?Math.round(n):Math.round(n*10)/10} MB`},QOt="Could not reach Decentraland's world server. Check your connection and try again.";function ZTr(t){try{const n=JSON.parse(t);if(typeof n=="object"&&n!==null){const{message:r,error:a}=n;if(typeof r=="string"&&r!=="")return r;if(typeof a=="string")return a}}catch{}return t.trim().slice(0,500)}function KTr(t,n,r){const a=ZTr(n).replace(/^(Deployment failed: )+/,""),s=(_,x,T=!1)=>new Wg(_,x,T,`${t} ${a}`);if(t===409)return s("changed",`${r} changed while this scene was being published. Nothing was replaced. Publish again.`,!0);if(t===413||/multipart request is too large/i.test(a))return s("too-large","This scene is too large to upload: a publish takes 350 MB at most. Remove or shrink its largest files.");if(t===408)return s("busy","The upload took too long and was stopped. Try again on a faster connection.",!0);if(t===429||t===503)return s("busy","Decentraland's world server is busy. Try again in a minute.",!0);if(t>=500)return s("busy",`Decentraland's world server had a problem (${t}). Try again in a minute.`,!0);if(/no permission to publish|does not have permission to deploy/i.test(a))return s("permission",`This wallet can't publish to ${r}. Publish to a NAME you own, or ask the owner of ${r} to add your wallet to its deployment list.`);if(/name deny list/i.test(a))return s("permission",`${r} can't be published to: the name is on Decentraland's deny list.`);const l=/maximum total size allowed is (\d+) bytes.*tried to upload (\d+)/i.exec(a);if(l!==null)return s("too-large",`This scene is ${JOt(Number(l[2]))} and ${r} has room for ${JOt(Number(l[1]))}. Remove or shrink files (models, textures, sounds), or free space in your other worlds.`);const f=/maximum allowed is (\d+) but the deployment has (\d+)/i.exec(a);if(f!==null)return s("too-large",`This scene has ${f[2]} files and a world takes ${f[1]} at most.`);const m=/Max allowed scene dimensions is (\d+) parcels/i.exec(a);return m!==null?s("too-large",`This scene covers more parcels than ${r} allows (${m[1]}). Use fewer parcels in Scene settings.`):/secs ago/i.test(a)?s("expired","The confirmation took more than five minutes, so the signature expired. Publish again.",!0):/in the future/i.test(a)?s("expired","This device's clock is ahead of the server's. Check the date and time, then publish again."):/Invalid entity hash|doesn't match the provided content|Extra file detected|neither present in the storage|Only CIDv1/i.test(a)?s("refused","The upload did not arrive intact. Publish again.",!0):/ephemeral|signature|auth ?chain|signer/i.test(a)?s("not-signed-in","Decentraland could not verify your signature. Sign in again, then publish again."):/thumbnail/i.test(a)?s("invalid-scene","The scene thumbnail is not one of the published files. Choose it again in Scene settings."):/worldConfiguration|scene\.json|pointers|parcel|must have required property|must be|is not present in the entity/i.test(a)?s("invalid-scene",`Decentraland did not accept this scene's settings: ${a}`):s("refused",`Decentraland refused this scene: ${a===""?`status ${t}`:a}`)}const ekr=5e7,YOt=1e4,tkr=/^-?\d+,-?\d+$/,mk=t=>new Wg("invalid-scene",t);function nkr(t,n){const r=new Map([...t,...n]),a=t.get(".dclignore"),s=yZe(r.keys(),a===void 0?void 0:new TextDecoder().decode(a)),l=new Map;for(const f of s.sort()){const m=r.get(f);m!==void 0&&f!==FR&&l.set(f,m)}return l}function rkr(t){const n=new Set(t),r=[t[0]];n.delete(t[0]);for(let a=r.shift();a!==void 0;a=r.shift()){const[s,l]=a.split(",").map(Number);for(const f of[`${s+1},${l}`,`${s-1},${l}`,`${s},${l+1}`,`${s},${l-1}`])n.delete(f)&&r.push(f)}return n.size===0}function ikr(t,n){var m,_,x;const r=(m=t.scene)==null?void 0:m.parcels,a=(_=t.scene)==null?void 0:_.base;if(!Array.isArray(r)||r.length===0||typeof a!="string")throw mk("This scene has no parcels. Set them in Scene settings.");if(r.some(T=>typeof T!="string"||!tkr.test(T)))throw mk('A parcel of this scene is not written as "x,y". Fix it in Scene settings.');if(new Set(r).size!==r.length)throw mk("A parcel is listed twice in this scene. Fix it in Scene settings.");if(!r.includes(a))throw mk(`The base parcel ${a} is not one of the scene's parcels. Fix it in Scene settings.`);if(!rkr(r))throw mk("The scene's parcels are not next to each other. Fix them in Scene settings.");const s=t.main;if(typeof s!="string"||!s.endsWith(".js"))throw mk('scene.json has no "main" script to run.');if(!n.has(s))throw mk(`The build did not produce ${s}, the script scene.json names.`);const l=(x=t.display)==null?void 0:x.navmapThumbnail;if(typeof l=="string"&&l!==""&&!n.has(l))throw mk(`The scene thumbnail “${l}” is not one of the files being published. Choose it again in Scene settings.`);const f=new Map;for(const[T,A]of n){const N=f.get(T.toLowerCase());if(N!==void 0)throw mk(`“${N}” and “${T}” differ only in capitals. A scene cannot hold both: rename one.`);if(f.set(T.toLowerCase(),T),A.length>ekr)throw mk(`“${T}” is larger than 50 MB, the most one file of a scene can be.`)}return r}async function akr(t){var N,L,B;const n=N0(t.files.get("scene.json"));if(n===null)throw mk("scene.json is missing or is not valid JSON.");if(((N=n.worldConfiguration)==null?void 0:N.name)!==t.world)throw mk(`scene.json does not name ${t.world} as its world.`);const r=nkr(t.files,t.outputs),a=ikr(n,r),{sdkVersion:s,...l}=n,f=new Map,m=[];for(const[G,Y]of r){const Z=await Gxe(Y);m.push({file:G,hash:Z}),f.set(Z,Y)}if(m.length>YOt)throw mk(`This scene has ${m.length.toLocaleString("en")} files and a World takes at most ${YOt.toLocaleString("en")}. Delete files it does not use in Assets.`);let _=0;for(const G of f.values())_+=G.length;const x={version:"v3",type:"scene",pointers:a,timestamp:t.timestamp,content:m,metadata:{sdkVersion:t.sdkVersion,...l}},T=new TextEncoder().encode(JSON.stringify(x)),A=await Gxe(T);return f.set(A,T),{entityId:A,entity:x,files:f,title:((B=(L=n.display)==null?void 0:L.title)==null?void 0:B.trim())||t.world,fileCount:m.length,bytes:_}}const skr=t=>`${Math.round(t/1024/1024)} MB`,okr=6,ckr=2e3;function lkr(t,n){var f,m;const r=typeof n.error=="string"?n.error:`status ${t}`,a=`${t} ${r}`,s=typeof n.retryAfter=="number"?n.retryAfter:null,l=s===null?"Try again in a minute.":s<90?`Try again in ${s} seconds.`:`Try again in ${Math.ceil(s/60)} minutes.`;switch(r){case"pool-unavailable":return new Wg("refused","Shared worlds aren't available right now.",!1,a);case"pool-not-allowed":return new Wg("permission","Shared worlds are open to a small group for now, and this wallet isn't in it yet.",!1,a);case"pool-blocked":return new Wg("permission","This wallet can no longer publish to shared worlds.",!1,a);case"pool-full":return new Wg("refused","The shared worlds have no room for another scene right now. Try again later, or get a NAME of your own.",!0,a);case"pool-busy":case"rate-limited":return new Wg("busy",`Too many publishes at once. ${l}`,!0,a);case"scene-too-large":return new Wg("too-large",`A scene in a shared world can be ${skr(Number(n.limit))} at most. Remove or shrink its largest files.`,!1,a);case"outside-zone":return new Wg("invalid-scene",`A scene in a shared world fits in ${Number((f=n.zone)==null?void 0:f.size)}×${Number((m=n.zone)==null?void 0:m.size)} parcels. Use fewer parcels in Scene settings.`,!1,a);case"parcels-taken":return new Wg("changed","Something else stands on your parcels of the shared world. Publish again.",!0,a);case"entity-timestamp":return new Wg("expired","This device's clock is off. Check the date and time, then publish again.",!1,a);case"no-lease":return new Wg("changed","Your place in the shared world was given back. Publish again to get a new one.",!0,a);default:return t>=500?new Wg("busy",`Shared worlds had a problem (${r}). ${l}`,!0,a):new Wg("refused",`The shared world refused this scene: ${r}`,!1,a)}}function ukr(t,n){async function r(a,s,l){let f;try{f=await n(`${t}${s}`,{method:a,headers:l===void 0?{}:{"content-type":"application/json"},body:l===void 0?null:JSON.stringify(l)})}catch(_){const x=_ instanceof Error?_.message:String(_);throw x==="not-signed-in"?new Wg("not-signed-in","Sign in to publish"):new Wg("network","Could not reach the editor service. Check your connection and try again.",!0,x)}const m=await f.json().catch(()=>({}));if(!f.ok&&f.status!==202)throw lkr(f.status,m);return{status:f.status,json:m}}return{async lease(){const{json:a}=await r("POST","/pool/lease");return a.lease},async sign(a,s){const{json:l}=await r("POST","/pool/sign",{entity:a,bytes:s});return l.authChain},async published(a){for(let s=0;s<okr;s++){if((await r("POST","/pool/published",{entityId:a})).status!==202)return;await new Promise(l=>setTimeout(l,ckr))}}}}const XOt=t=>t.split(",").map(Number);function dkr(t,n){var L,B;const r=N0(t),a=(L=r==null?void 0:r.scene)==null?void 0:L.parcels,s=(B=r==null?void 0:r.scene)==null?void 0:B.base;if(r===null||!Array.isArray(a)||a.length===0||typeof s!="string")throw new Wg("invalid-scene","This scene has no parcels. Set them in Scene settings.");const l=a.map(XOt),f=Math.min(...l.map(([G])=>G)),m=Math.min(...l.map(([,G])=>G)),_=Math.max(...l.map(([G])=>G))-f+1,x=Math.max(...l.map(([,G])=>G))-m+1,{size:T}=n.zone;if(_>T||x>T)throw new Wg("invalid-scene",`A scene in a shared world fits in ${T}×${T} parcels, and this one spans ${_}×${x}. Use fewer parcels in Scene settings.`);const A=G=>{const[Y,Z]=XOt(G);return`${Y-f+n.zone.x},${Z-m+n.zone.y}`},N={...r,scene:{...r.scene,parcels:a.map(A),base:A(s)},worldConfiguration:n.worldConfiguration};return new TextEncoder().encode(JSON.stringify(N,null,2))}const ZOt=100,fkr=3,pkr=1500,aqe=t=>t instanceof Error&&t.name==="AbortError",KOt=()=>new Wg("cancelled","Publishing was cancelled.");function mkr(t,n,r){const a=new FormData;a.append("entityId",t),n.forEach((s,l)=>{a.append(`authChain[${l}][type]`,s.type),a.append(`authChain[${l}][payload]`,s.payload),a.append(`authChain[${l}][signature]`,s.signature)});for(const[s,l]of r)a.append(s,new Blob([l]),s);return a}async function hkr(t,n,r){const a=new Set;try{for(let s=0;s<n.length;s+=ZOt){const l=n.slice(s,s+ZOt).map(_=>`cid=${_}`).join("&"),f=await fetch(`${t}/available-content?${l}`,{signal:r});if(!f.ok)return new Set;const m=await f.json();if(!Array.isArray(m))return new Set;for(const _ of m)typeof _.cid=="string"&&_.available===!0&&a.add(_.cid)}}catch(s){if(aqe(s))throw s;return new Set}return a}function _kr(t,n){if(!Array.isArray(t)||t.length<2)return!1;const r=t;return r.every(a=>typeof(a==null?void 0:a.type)=="string"&&typeof a.payload=="string"&&typeof a.signature=="string")?r[0].type==="SIGNER"&&r[r.length-1].payload===n:!1}async function gkr(t){var s,l;const{deployment:n,server:r,signal:a}=t;try{const f=[...n.files],m=await hkr(r,[...n.files.keys()],a);let _=f.filter(([N])=>N===n.entityId||!m.has(N));const x=_.reduce((N,[,L])=>N+L.length,0),T=`${Math.ceil(x/1024)} KB`;(s=t.onProgress)==null||s.call(t,_.length===f.length?`Uploading ${f.length-1} files (${T}).`:`Uploading ${_.length-1} of ${f.length-1} files (${T}): the server already has the others.`);let A=new Wg("network",QOt,!0);for(let N=1;N<=fkr;N++){if(N>1&&((l=t.onRetry)==null||l.call(t,N,A.detail??A.message),await new Promise(G=>setTimeout(G,pkr*(N-1))),(a==null?void 0:a.aborted)===!0))throw KOt();let L;try{L=await fetch(`${r}/entities`,{method:"POST",body:mkr(n.entityId,t.authChain,_),signal:a})}catch(G){if(aqe(G))throw G;A=new Wg("network",QOt,!0,G instanceof Error?G.message:String(G));continue}const B=await L.text();if(L.ok)try{const G=JSON.parse(B).message;return typeof G=="string"?G:""}catch{return""}if(A=KTr(L.status,B,t.world),_.length<f.length&&/neither present in the storage/.test(A.detail??"")){_=f;continue}if(L.status<500&&L.status!==408&&L.status!==429)throw A}throw A}catch(f){throw aqe(f)?KOt():f}}const zq=100,fqt=20;function vZe(t){const n=new URL(t).hostname;return n.endsWith(".decentraland.org")?"https://peer.decentraland.org/lambdas":n.endsWith(".decentraland.zone")?"https://peer.decentraland.zone/lambdas":null}function ykr(t,n){return vZe(t)===null?`${t}/world/${n}`:n}function vkr(t,n,r){const a=`${t}?realm=${encodeURIComponent(ykr(n,r))}`;if(vZe(n)===null)return a;const s=new URL(n).hostname.split(".").slice(-2).join("."),l=new URL(t).hostname;return l===s||l.endsWith(`.${s}`)?a:`${a}&baseDomain=${s}`}async function bkr(t,n){const r=[];for(let a=1;a<=fqt;a++){const s=await fetch(`${t}/users/${n}/names?pageSize=${zq}&pageNum=${a}`);if(!s.ok)throw new Error(`could not list your NAMEs (${s.status})`);const l=await s.json(),f=Array.isArray(l.elements)?l.elements:[];for(const{name:m}of f)typeof m=="string"&&m!==""&&r.push(`${m.toLowerCase()}.dcl.eth`);if(f.length<zq||typeof l.totalAmount=="number"&&r.length>=l.totalAmount)break}return r}async function xkr(t,n){const r=[];for(let a=0;a<fqt;a++){const s=await fetch(`${t}/worlds?authorized_deployer=${n}&limit=${zq}&offset=${a*zq}`);if(!s.ok)throw new Error(`could not list the worlds you can publish to (${s.status})`);const l=await s.json(),f=Array.isArray(l.worlds)?l.worlds:[];for(const m of f){if(typeof m.name!="string")continue;const _=typeof m.owner=="string"&&m.owner.toLowerCase()===n;r.push({name:m.name.toLowerCase(),role:_?"owner":"collaborator"})}if(f.length<zq||typeof l.total=="number"&&(a+1)*zq>=l.total)break}return r}async function wkr(t,n){if(t===null)return[];const r=t.toLowerCase(),a=n.replace(/\/+$/,""),s=vZe(a),[l,f]=await Promise.all([s===null?[]:bkr(s,r),xkr(a,r).catch(_=>{if(s===null)throw _;return[]})]),m=new Map;for(const _ of f)m.set(_.name,_);for(const _ of l)m.set(_,{name:_,role:"owner"});return[...m.values()].sort((_,x)=>_.name.localeCompare(x.name))}const ePt=t=>t instanceof Error?t.message:String(t),Mve=t=>t<1024*1024?`${Math.ceil(t/1024)} KB`:`${(t/1024/1024).toFixed(1)} MB`,Nne={ok:!1,reason:"cancelled",message:"Publishing was cancelled.",retryable:!0},tPt={ok:!1,reason:"not-signed-in",message:"Sign in to publish",retryable:!1},Oj=(t,n)=>({ok:!1,reason:"failed",message:t,retryable:n});class Skr{constructor(){this.code="unexpected"}set(n){return this.code=n.replace(/-/g,"_"),this}}function Tkr(t){const{host:n}=t,r=n.services.projects,a=t.pool??(r===null?void 0:ukr(r,(x,T)=>n.signedFetch(x,T))),s=n.services.worldsContent.replace(/\/+$/,"");let l=null;function f(x){var A;const T=(A=x.entity.metadata.scene)==null?void 0:A.base;return typeof T=="string"?`&position=${encodeURIComponent(T)}`:""}async function m(x,T,A,N,L){const{address:B,isGuest:G}=n.identity();if(G||B===null)return tPt;let Y=null;T===null&&a!==void 0?(A({step:"building",line:"Finding you a place in a shared world…"}),Y=await a.lease(),A({step:"building",line:`${Y.world}, parcels from ${Y.zone.x},${Y.zone.y} (${Y.zone.size}×${Y.zone.size})`})):T!==null&&await t.setWorld(x,T);const Z=(Y==null?void 0:Y.world)??T;if(N.aborted)return Nne;A({step:"building",line:"Building the scene for publishing…"});const te=await t.files(x),pe=await t.build(x,te);if(N.aborted)return Nne;if(!pe.ok){for(const lt of pe.diagnostics.filter(St=>St.severity==="error"))A({step:"building",line:lt.file===void 0?`ERROR: ${lt.message}`:`${lt.file}:${lt.line??1}:${lt.column??0}: ERROR: ${lt.message}`});const xt=pe.events.find(lt=>lt.type==="failed");return(xt==null?void 0:xt.type)==="failed"&&pe.diagnostics.length===0&&A({step:"building",line:xt.message}),L.set("build"),Oj("The build failed.",!1)}const fe=Y===null?te:new Map([...te,["scene.json",dkr(te.get("scene.json"),Y)]]),X=await akr({files:fe,outputs:pe.outputs,world:Z,sdkVersion:pe.sdkVersion,timestamp:Date.now()});let he;if(Y!==null&&a!==void 0){if(X.bytes>Y.maxSceneBytes)return L.set("too_large"),Oj(`This scene is ${Mve(X.bytes)} and a scene in a shared world can be ${Mve(Y.maxSceneBytes)} at most. Remove or shrink its largest files.`,!1);A({step:"signing",line:`Asking the shared world to sign ${X.fileCount} files, ${Mve(X.bytes)}, as ${X.entityId}`});const xt=X.files.get(X.entityId);he=await a.sign(new TextDecoder().decode(xt),X.bytes)}else{A({step:"signing",line:`Waiting for your confirmation: ${X.fileCount} files, ${Mve(X.bytes)}, as ${X.entityId}`});try{he=await n.signDeployment({world:Z,entityId:X.entityId,title:X.title,fileCount:X.fileCount,bytes:X.bytes})}catch(xt){const lt=ePt(xt);return lt==="cancelled"?Nne:lt==="not-signed-in"?tPt:(L.set("sign_failed"),Oj(`Decentraland could not sign this publish: ${lt}`,!0))}}if(N.aborted)return Nne;if(!_kr(he,X.entityId))return L.set("no_signature"),Oj("Decentraland did not return a signature for this scene.",!0);A({step:"uploading",line:`Uploading to ${Z} (${s})…`});const Ee=await gkr({server:s,world:Z,deployment:X,authChain:he,signal:N,onProgress:xt=>A({step:"uploading",line:xt}),onRetry:(xt,lt)=>A({step:"uploading",line:`Trying again (${xt} of 3): ${lt}`})});Ee!==""&&A({step:"uploading",line:Ee}),Y!==null&&await(a==null?void 0:a.published(X.entityId).catch(xt=>console.warn("[editor] the shared world did not confirm the publish",xt)));const qe={world:Z,entityId:X.entityId,url:`${vkr(n.pageDir,s,Z)}${f(X)}`,files:X.fileCount,bytes:X.bytes,at:Date.now()};return await t.published(x,qe).catch(xt=>console.warn("[editor] could not record the publish",xt)),{ok:!0,published:qe}}async function _(x,T,A){if(l!==null)return Oj("A scene is already being published.",!1);const N=new AbortController;l=N;const L=new Skr,B=await m(x,T,A,N.signal,L).catch(Z=>Z instanceof Wg?Z.reason==="cancelled"?Nne:(Z.detail!==void 0&&A({step:"uploading",line:`The server answered: ${Z.detail}`}),L.set(Z.reason),Oj(Z.message,Z.retryable)):Oj(ePt(Z),!0)).finally(()=>{l=null}),G=B.ok?B.published.world:T??"shared",Y=T===null;return B.ok?tw("scene_published",{project_id:x,world:G,shared:Y,files:B.published.files,bytes:B.published.bytes}):tw("publish_failed",{project_id:x,world:G,shared:Y,error:B.reason==="failed"?L.code:B.reason.replace(/-/g,"_"),retryable:B.retryable}),B}return{publish:(x,T,A)=>_(x,T,A),...a===void 0?{}:{publishShared:(x,T)=>_(x,null,T)},stop(){l==null||l.abort()}}}function kkr(){return{root:()=>navigator.storage.getDirectory(),locks:{request:(t,n,r)=>navigator.locks.request(t,n,r)}}}function yI(t){return t instanceof Error&&t.name==="NotFoundError"}const nPt=t=>encodeURIComponent(t);function rPt(t){const n=t.split("/");if(t===""||n.some(r=>r===""||r==="."||r===".."))throw new Error(`Invalid package path "${t}"`);return n}async function*pqt(t,n=""){for await(const[r,a]of t.entries())a.kind==="file"?yield n+r:yield*pqt(await t.getDirectoryHandle(r),`${n}${r}/`)}const Lve=(t,n)=>{if(yI(t))return n;throw t};class Ekr{constructor(n){this.root=n}async dir(n,r=!1){return(await this.root()).getDirectoryHandle(nPt(n),{create:r})}async has(n){return this.dir(n).then(()=>!0,r=>Lve(r,!1))}async list(n){try{const r=[];for await(const a of pqt(await this.dir(n)))r.push(a);return r}catch(r){return Lve(r,[])}}async read(n,r){try{const a=rPt(r);let s=await this.dir(n);for(const f of a.slice(0,-1))s=await s.getDirectoryHandle(f);const l=await s.getFileHandle(a[a.length-1]);return new Uint8Array(await(await l.getFile()).arrayBuffer())}catch(a){return Lve(a,void 0)}}async write(n,r,a){const s=rPt(r);let l=await this.dir(n,!0);for(const m of s.slice(0,-1))l=await l.getDirectoryHandle(m,{create:!0});const f=await(await l.getFileHandle(s[s.length-1],{create:!0})).createWritable();await f.write(a),await f.close()}async remove(n){await(await this.root()).removeEntry(nPt(n),{recursive:!0}).catch(r=>Lve(r,void 0))}async keys(){const n=[];for await(const[r,a]of(await this.root()).entries())a.kind==="directory"&&n.push(decodeURIComponent(r));return n}}class Yxe extends Error{constructor(n){super(`"${n}" is open in another tab`),this.name="ProjectBusyError"}}const Akr="dcl-editor",iPt="index.json",Ckr="dcl-editor-index",aPt=t=>`dcl-editor-project:${t}`,mqt=/[\u200e\u200f\u202a-\u202e\u2066-\u2069]/;function sqe(t){if(t===""||/[\\\0]/.test(t))return null;const n=t.split("/");return n.some(r=>r===""||r==="."||r==="..")?null:n}function iJ(t){const n=sqe(t);return n===null||mqt.test(t)||n[0]==="bin"?!1:!n.some(r=>{const a=r.toLowerCase();return a==="node_modules"||a===".git"||a.startsWith(".env")})}function Okr(t){return t.trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"").slice(0,48).replace(/-$/,"")||"my-scene"}function sPt(){return Array.from(crypto.getRandomValues(new Uint8Array(4)),t=>(t%36).toString(36)).join("")}async function Fve(t){return new Uint8Array(await(await t.getFile()).arrayBuffer())}async function A9e(t,n){const r=await t.createWritable();await r.write(n),await r.close()}async function*oqe(t,n=""){for await(const[r,a]of t.entries())a.kind==="file"?yield[n+r,await t.getFileHandle(r)]:yield*oqe(await t.getDirectoryHandle(r),`${n}${r}/`)}function Pkr(t){return typeof t=="object"&&t!==null&&Array.isArray(t.projects)}class Ikr{constructor(n){this.backend=n,this.held=new Map,this.queue=Promise.resolve(),this.packages=new Ekr(async()=>(await this.base()).getDirectoryHandle("packages",{create:!0}))}async base(){return(await this.backend.root()).getDirectoryHandle(Akr,{create:!0})}async projects(){return(await this.base()).getDirectoryHandle("projects",{create:!0})}async dir(n,r=!1){if(!hZe.test(n))throw new Error(`Invalid project id "${n}"`);return(await this.projects()).getDirectoryHandle(n,{create:r})}async records(){let n;try{n=await(await this.base()).getFileHandle(iPt)}catch(s){if(yI(s))return[];throw s}const r=await Fve(n);if(r.length===0)return[];const a=JSON.parse(new TextDecoder().decode(r));if(!Pkr(a))throw new Error("The project index is not readable");return a.projects}mutate(n){return this.backend.locks.request(Ckr,{},async()=>{const r=await this.records(),a=await n(r),s=await(await this.base()).getFileHandle(iPt,{create:!0});return await A9e(s,new TextEncoder().encode(JSON.stringify({version:1,projects:r}))),a})}serial(n){const r=this.queue.then(n,n);return this.queue=r.catch(()=>{}),r}locked(n,r){return this.held.has(n)?r():this.serial(()=>this.backend.locks.request(aPt(n),{ifAvailable:!0},async a=>{if(a===null)throw new Yxe(n);return r()}))}async put(n,r,a){const s=sqe(r)??[];let l=n;for(const f of s.slice(0,-1))l=await l.getDirectoryHandle(f,{create:!0});await A9e(await l.getFileHandle(s[s.length-1],{create:!0}),a)}async file(n,r){const a=sqe(r);if(a===null)throw new Error(`Invalid project path "${r}"`);let s=await this.dir(n);for(const l of a.slice(0,-1))s=await s.getDirectoryHandle(l);return{folder:s,name:a[a.length-1]}}async list(){return(await this.records()).filter(n=>n.deletedAt===null)}async trashed(){return(await this.records()).filter(n=>n.deletedAt!==null)}create(n,r,a={}){return this.mutate(async s=>{const l=new Set([...s.map(A=>A.id),...a.taken??[]]),f=Okr(n);let m=a.id??`${f}-${sPt()}`;if(a.id!==void 0&&s.some(A=>A.id===m))throw new Error(`"${m}" already exists`);for(;a.id===void 0&&l.has(m);)m=`${f}-${sPt()}`;await this.writeJson("sync",m,null),await(await this.projects()).removeEntry(m,{recursive:!0}).catch(A=>{if(!yI(A))throw A});const _=await this.dir(m,!0);for(const[A,N]of r)iJ(A)&&await this.put(_,A,N);const x=Date.now(),T={id:m,name:n.trim(),createdAt:x,lastOpened:x,favourite:!1,deletedAt:null};return s.push(T),T})}async duplicate(n,r,a){return this.create(r,await this.readAll(n),{taken:a})}async writeJson(n,r,a){const s=await(await this.base()).getDirectoryHandle(n,{create:!0});if(a!==null){await A9e(await s.getFileHandle(`${r}.json`,{create:!0}),new TextEncoder().encode(JSON.stringify(a)));return}await s.removeEntry(`${r}.json`).catch(l=>{if(!yI(l))throw l})}async readJson(n,r){try{const a=await(await this.base()).getDirectoryHandle(n);return JSON.parse(new TextDecoder().decode(await Fve(await a.getFileHandle(`${r}.json`))))}catch(a){if(yI(a)||a instanceof SyntaxError)return null;throw a}}syncRecord(n){return this.readJson("sync",n)}transcript(n,r){return this.readJson("assistant",`${n}@${r.toLowerCase()}`)}setTranscript(n,r,a){return this.writeJson("assistant",`${n}@${r.toLowerCase()}`,a)}assistantMemory(n,r){return this.readJson("assistant",`${n}@${r.toLowerCase()}.memory`)}setAssistantMemory(n,r,a){return this.writeJson("assistant",`${n}@${r.toLowerCase()}.memory`,a)}async removeTranscripts(n){let r;try{r=await(await this.base()).getDirectoryHandle("assistant")}catch(s){if(yI(s))return;throw s}const a=[];for await(const[s]of r.entries())s.startsWith(`${n}@`)&&a.push(s);for(const s of a)await r.removeEntry(s)}setSyncRecord(n,r){return this.writeJson("sync",n,r)}patch(n,r){return this.mutate(a=>{const s=a.find(l=>l.id===n);s!==void 0&&Object.assign(s,r)})}async trash(n){if(this.held.has(n))return!1;try{return await this.locked(n,()=>this.mutate(r=>{const a=r.find(s=>s.id===n&&s.deletedAt===null);return a===void 0?!1:(a.deletedAt=Date.now(),a.favourite=!1,!0)}))}catch(r){if(r instanceof Yxe)return!1;throw r}}restore(n){return this.mutate(r=>{const a=r.find(s=>s.id===n&&s.deletedAt!==null);return a===void 0?!1:(a.deletedAt=null,!0)})}purge(n){return this.mutate(async r=>{const a=r.filter(s=>s.deletedAt!==null&&s.deletedAt<n);for(const{id:s}of a)await(await this.projects()).removeEntry(s,{recursive:!0}).catch(l=>{if(!yI(l))throw l}),await this.writeJson("sync",s,null),await this.removeTranscripts(s),r.splice(r.findIndex(l=>l.id===s),1);return a.map(({id:s,previewId:l})=>({id:s,previewId:l}))})}open(n){return this.held.has(n)?Promise.resolve(!0):this.serial(()=>new Promise((r,a)=>{this.backend.locks.request(aPt(n),{ifAvailable:!0},s=>s===null?(r(!1),Promise.resolve()):new Promise(l=>{this.held.set(n,l),r(!0)})).catch(a)}))}close(n){var r;(r=this.held.get(n))==null||r(),this.held.delete(n)}holds(n){return this.held.has(n)}async read(n,r){const{folder:a,name:s}=await this.file(n,r);return Fve(await a.getFileHandle(s))}async paths(n){const r=[];for await(const[a]of oqe(await this.dir(n)))r.push(a);return r.sort()}async readAll(n){const r=new Map;for await(const[a,s]of oqe(await this.dir(n)))r.set(a,await Fve(s));return r}write(n,r,a){return iJ(r)?this.locked(n,async()=>this.put(await this.dir(n),r,a)):Promise.reject(new Error(`"${r}" is not kept in a project`))}remove(n,r){return this.locked(n,async()=>{try{const{folder:a,name:s}=await this.file(n,r);return await a.removeEntry(s),!0}catch(a){if(yI(a))return!1;throw a}})}}class bZe extends Error{constructor(n){super("The scene was changed on another device"),this.version=n,this.name="SyncConflictError"}}class hae extends Error{constructor(){super("The scene is in the trash on your account"),this.name="SyncDeletedError"}}class tO extends Error{constructor(n,r,a){super(a),this.status=n,this.code=r,this.name="SyncHttpError"}}function jve(t){return t instanceof tO?t.code==="missing-blobs"?"missing-content":t.status>=500||t.status===408||t.status===429?"retry":"rejected":t instanceof bZe||t instanceof hae?"rejected":t instanceof Error&&t.message==="not-signed-in"?"signed-out":"retry"}const cqe=t=>typeof t=="number"?`${Math.round(t/(1024*1024))} MB`:"the limit";function Nkr(t,n,r){switch(n){case"quota-exceeded":return`Your account's scene storage is full (${cqe(r.limit)}). A scene in the trash frees its space 30 days after it was trashed. This one stays on this device.`;case"file-too-large":return`A file in this scene is larger than the ${cqe(r.limit)} an account stores per file. The scene stays on this device.`;case"too-many-files":return`This scene has more files than an account stores (${String(r.limit)}). It stays on this device.`;case"too-many-projects":return`Your account already holds ${String(r.limit)} scenes, counting the ones in its trash, which stop counting 30 days after they were trashed. This one stays on this device.`;case"invalid-path":return`"${String(r.path)}" cannot be stored on your account. The scene stays on this device.`;case"origin-not-allowed":return"Scene sync is not available from this address."}return t===401||t===403?"Your account did not accept this sign-in. Sign in again to sync your scenes.":n===""?`The project service answered ${t}`:`The project service answered ${t} (${n})`}async function e0e(t){const n=await t.json().catch(()=>null),r=typeof n=="object"&&n!==null?n:{},a=typeof r.error=="string"?r.error:"";return new tO(t.status,a,Nkr(t.status,a,r))}function hqt(t,n,r=(a,s)=>fetch(a,s)){const a={"content-type":"application/json"};async function s(m,_){const x=await n(t+m,_);if(x.ok)return x;const T=await e0e(x);throw T.code==="deleted"?new hae:T}const l=m=>m instanceof tO&&m.status===404;let f=null;return{async list(){const m=await(await s("/projects")).json(),{bytes:_,limit:x}=m.usage??{};return f=typeof _=="number"&&typeof x=="number"?{bytes:_,limit:x}:null,Array.isArray(m.projects)?m.projects:[]},usage:()=>f,async manifest(m){try{return await(await s(`/projects/${m}/manifest`)).json()}catch(_){if(l(_))return null;throw _}},async uploads(m,_){if(_.length===0)return[];const T=await(await s(`/projects/${m}/uploads`,{method:"POST",headers:a,body:JSON.stringify({files:_})})).json();return Array.isArray(T.uploads)?T.uploads:[]},async putBlob(m,_){const x=await r(m.url,{method:m.method,headers:m.headers,body:_});if(!x.ok)throw await e0e(x)},async putManifest(m,_){const x=await n(`${t}/projects/${m}/manifest`,{method:"PUT",headers:a,body:JSON.stringify(_)});if(x.status===409||x.status===412){const A=await x.json().catch(()=>({}));throw A.error==="deleted"?new hae:new bZe(A.version??null)}if(!x.ok)throw await e0e(x);const{version:T}=await x.json().catch(()=>({}));if(typeof T!="number")throw new tO(502,"","The project service did not say which version it saved");return T},async blob(m,_){return new Uint8Array(await(await s(`/projects/${m}/files/${_}`)).arrayBuffer())},async remove(m){await s(`/projects/${m}`,{method:"DELETE"}).catch(_=>{if(!l(_))throw _})},async restore(m){await s(`/projects/${m}/restore`,{method:"POST"})}}}class _qt extends Error{}function Dkr(t,n){return t.code==="quota-exceeded"?new tO(t.status,t.code,`Your account's storage is full (${cqe(n.limit)}). The prefab stays in this scene.`):t.code==="too-many-prefabs"?new tO(t.status,t.code,`Your library already holds ${String(n.limit)} prefabs. Remove one to add another.`):t}function Rkr(t,n,r=(a,s)=>fetch(a,s)){const a=hqt(t,n,r),s={"content-type":"application/json"};async function l(m,_){const x=await n(t+m,_);if(x.ok)return x;const T=await x.clone().json().catch(()=>({}));throw Dkr(await e0e(x),T)}const f=m=>`/prefabs/${encodeURIComponent(m)}`;return{async list(){const m=await(await l("/prefabs")).json();return Array.isArray(m.prefabs)?m.prefabs:[]},async uploads(m,_){if(_.length===0)return[];const T=await(await l(`${f(m)}/uploads`,{method:"POST",headers:s,body:JSON.stringify({files:_})})).json();return Array.isArray(T.uploads)?T.uploads:[]},putBlob:(m,_)=>a.putBlob(m,_),async put(m,_){try{await l(`${f(m)}/manifest`,{method:"PUT",headers:s,body:JSON.stringify({files:_})})}catch(x){throw x instanceof tO&&x.code==="exists"?new _qt(m):x}},async blob(m,_){return new Uint8Array(await(await l(`${f(m)}/files/${_}`)).arrayBuffer())},async remove(m){try{return await l(f(m),{method:"DELETE"}),!0}catch(_){if(_ instanceof tO&&_.status===404)return!1;throw _}}}}class lqe extends Error{}async function gqt(t,n,r){try{return await t.read(n,r)}catch(a){if(yI(a))return null;throw a}}async function Bve(t,n,r,a,s){const l={...a};for(const f of a===null?await t.paths(r):s){const m=iJ(f)?await gqt(t,r,f):null;m===null?delete l[f]:l[f]={hash:await n(m),size:m.length}}return l}function oPt(t,n){const r=Object.keys(t);return r.length===Object.keys(n).length&&r.every(a=>{var s;return((s=n[a])==null?void 0:s.hash)===t[a].hash})}async function cPt(t,n,r,a,s,l){const f=new Set(Object.values(s).map(T=>T.hash)),m=new Map;for(const[T,A]of Object.entries(a))f.has(A.hash)||m.set(A.hash,{path:T,file:A});const _=await n.uploads(r,[...m.values()].map(T=>T.file));l(0,_.length);let x=0;for(const T of _){const A=m.get(T.hash),N=A===void 0?null:await gqt(t,r,A.path);if(N===null||N.length!==(A==null?void 0:A.file.size))throw new lqe;try{await n.putBlob(T,N)}catch(L){throw L instanceof tO&&[400,403,422].includes(L.status)?new lqe:L}l(++x,_.length)}}async function lPt(t,n,r,a){const s=new Map,l=new Map;for(const[f,m]of r){const _=s.get(m.hash)??await t.blob(n,m.hash);s.set(m.hash,_),l.set(f,_),a(l.size,r.length)}return l}async function Uve(t,n){try{return await t.manifest(n)}catch(r){if(!(r instanceof hae))throw r;return await t.restore(n),t.manifest(n)}}const C9e=2e3,uPt=2e3,dPt=6e4,Mkr=4e3,Lkr=5e3,Fkr=3,fPt=["conflict","error","offline","syncing","synced","local"],pPt=t=>t instanceof Error?t.message:String(t);function mPt(t){const n=t;return typeof n=="object"&&n!==null&&typeof n.wallet=="string"&&typeof n.baseVersion=="number"&&typeof n.base=="object"&&n.base!==null&&Array.isArray(n.pending)}function jkr(t){const{store:n}=t,r=new Map,a=new Set;let s=new Set,l=[],f=new Set,m=0,_=!1,x=null,T=null,A=0,N=0,L=null,B=null,G=0,Y=!1,Z=!1;const te=new Set;function pe(){Z||Y||(Z=!0,queueMicrotask(()=>{if(Z=!1,Y)return;const Re=qt.status();for(const Ke of a)Ke(Re)}))}function fe(Re){const Ke=Re.then(()=>{},()=>{});return te.add(Ke),Ke.then(()=>te.delete(Ke)),Re}function X(Re){const Ke=r.get(Re);if(Ke!==void 0)return Ke;const st={loaded:Promise.resolve(),record:null,pending:new Set,busy:0,conflict:!1,behind:!1,error:null,attempt:0,moved:0,retry:null,debounce:null,chain:Promise.resolve(),saving:Promise.resolve(),agreedAt:0};return st.loaded=he(Re,st),r.set(Re,st),st}async function he(Re,Ke){try{await Ke.saving;const st=await n.syncRecord(Re);if(Ke.record=mPt(st)?{...st,remoteDeleted:st.remoteDeleted===!0}:null,mPt(st))for(const tn of st.pending)Ke.pending.add(tn)}catch(st){console.warn(`[editor] could not read the sync record of ${Re}`,st)}}function Ee(Re,Ke){const st=Ke.saving.then(()=>n.setSyncRecord(Re,Ke.record===null?null:{...Ke.record,pending:[...Ke.pending]}));return Ke.saving=st.catch(tn=>console.warn(`[editor] could not save the sync record of ${Re}`,tn)),Ke.saving}function qe(Re,Ke){const st=X(Re),tn=st.chain.then(async()=>{await st.loaded;const Cn=t.session();if(!(Cn===null||Y)&&!(st.record!==null&&st.record.wallet!==Cn.wallet)){st.busy++,pe();try{return await Ke(st,Cn)}finally{st.busy--,st.progress=void 0,pe()}}});return st.chain=tn.catch(()=>{}),fe(tn)}function xt(Re){var st;const Ke=((st=t.session())==null?void 0:st.wallet)??null;return Re.record!==null&&Re.record.wallet!==Ke}function lt(Re){Re.debounce!==null&&clearTimeout(Re.debounce),Re.retry!==null&&clearTimeout(Re.retry),Re.debounce=null,Re.retry=null}function St(Re,Ke){const st=X(Re);st.debounce!==null&&clearTimeout(st.debounce),st.debounce=setTimeout(()=>{st.debounce=null,$e(Re)},Ke)}function ht(Re,Ke,st,tn){if(st instanceof bZe){Ke.conflict=!0;return}if(st instanceof lqe&&++Ke.moved<Fkr){St(Re,C9e);return}const Cn=jve(st);if(Cn!=="signed-out"){if(Cn==="retry"){_=!0,Ke.attempt++,Ke.retry!==null&&clearTimeout(Ke.retry),Ke.retry=setTimeout(()=>{Ke.retry=null,tn()},Math.min(dPt,uPt*2**(Ke.attempt-1)));return}Ke.error=pPt(st)}}function Ue(Re){_=!1,Re.error=null,Re.attempt=0,Re.moved=0,Re.retry!==null&&clearTimeout(Re.retry),Re.retry=null}const Wt=(Re,Ke)=>(st,tn)=>{Re.progress={done:st,total:tn},Ke==null||Ke(st,tn),pe()};function $e(Re){return qe(Re,async(Ke,{api:st,wallet:tn})=>{var un,Tn,fi;if(Ke.conflict||s.has(Re)||!f.has(Re)||Ke.record!==null&&Ke.pending.size===0)return;const Cn=[...Ke.pending];Ke.pending.clear();try{const Tt=((un=Ke.record)==null?void 0:un.base)??{},Wi=await Bve(n,t.hash,Re,((Tn=Ke.record)==null?void 0:Tn.base)??null,Cn);if(Ke.record===null||!oPt(Wi,Tt)){const Er={name:await t.title(Re),files:Wi,baseVersion:((fi=Ke.record)==null?void 0:fi.baseVersion)??null},to=async Vo=>(await cPt(n,st,Re,Wi,Vo,Wt(Ke)),st.putManifest(Re,Er)),Ba=await to(Tt).catch(async Vo=>{if(Vo instanceof hae)return await st.restore(Re),to(Tt);if(jve(Vo)==="missing-content")return to({});throw Vo});Ke.record={wallet:tn,baseVersion:Ba,base:Wi,pending:[],remoteDeleted:!1},Ke.agreedAt=++G}Ue(Ke),await Ee(Re,Ke),Ke.pending.size>0&&St(Re,C9e)}catch(Tt){for(const Wi of Cn)Ke.pending.add(Wi);if(Tt instanceof tO&&Tt.status===404&&Ke.record!==null){Ke.record=null,await Ee(Re,Ke),St(Re,0);return}ht(Re,Ke,Tt,()=>void $e(Re))}}).then(()=>{})}async function Me(Re,Ke,st,tn,Cn,un){const Tn=Object.entries(tn.files).filter(([Tt,Wi])=>{var Er;return iJ(Tt)&&((Er=Cn[Tt])==null?void 0:Er.hash)!==Wi.hash}),fi=await lPt(st.api,Re,Tn,Wt(Ke,un));for(const[Tt,Wi]of fi)await n.write(Re,Tt,Wi);for(const Tt of Object.keys(Cn))tn.files[Tt]===void 0&&await n.remove(Re,Tt);Ke.record={wallet:st.wallet,baseVersion:tn.version,base:tn.files,pending:[],remoteDeleted:!1},Ke.agreedAt=++G,Ke.pending.clear(),Ke.behind=!1,Ke.conflict=!1,Ue(Ke),await Ee(Re,Ke),m++}async function $t(Re,Ke,st,tn){var un;const Cn=await Bve(n,t.hash,Re,((un=Ke.record)==null?void 0:un.base)??null,[...Ke.pending]);return oPt(Cn,tn.files)?(Ke.record={wallet:st,baseVersion:tn.version,base:tn.files,pending:[],remoteDeleted:!1},Ke.agreedAt=++G,Ke.pending.clear(),Ke.conflict=!1,Ue(Ke),await Ee(Re,Ke),!0):!1}function vt(Re,Ke){return qe(Re,async(st,tn)=>{if(!(st.record===null||st.pending.size>0||st.conflict))try{const Cn=Ke??await Uve(tn.api,Re);if(Cn===null||Cn.version===st.record.baseVersion){st.behind=!1;return}await Me(Re,st,tn,Cn,st.record.base)}catch(Cn){Cn instanceof Yxe||ht(Re,st,Cn,()=>void vt(Re))}}).then(()=>{})}async function wt(Re,Ke,st,tn){await qe(Re,async(un,{api:Tn,wallet:fi})=>{const Tt=un.record;try{if(Ke){if(Tt===null||st===void 0)return;st.deletedAt!=null?Tt.remoteDeleted=!0:Tt.remoteDeleted?(await n.restore(Re),Tt.remoteDeleted=!1,m++):(await Tn.remove(Re),Tt.remoteDeleted=!0),await Ee(Re,un);return}if(st===void 0){if(Tt!==null&&un.agreedAt>tn)return;Tt!==null&&(un.record=null,await Ee(Re,un)),St(Re,0);return}if(Tt===null){un.conflict=!0;return}if(st.deletedAt!=null){if(!Tt.remoteDeleted&&un.pending.size===0&&!n.holds(Re)){await n.trash(Re)&&(Tt.remoteDeleted=!0,await Ee(Re,un),m++);return}await Tn.restore(Re),Tt.remoteDeleted=!1,await Ee(Re,un)}if(st.version!==Tt.baseVersion){if(un.pending.size===0)un.behind=!0;else{const Wi=await Uve(Tn,Re);(Wi==null?void 0:Wi.version)===Tt.baseVersion?St(Re,0):(Wi===null||!await $t(Re,un,fi,Wi))&&(un.conflict=!0)}return}un.behind=!1,un.pending.size>0&&St(Re,0)}catch(Wi){ht(Re,un,Wi,()=>void qt.refresh(!0))}}),X(Re).behind&&!n.holds(Re)&&await vt(Re)}async function dt(){var Tn;const Re=((Tn=t.session())==null?void 0:Tn.wallet)??null;if(Re!==B){B=Re,_=!1,x=null;for(const fi of r.values())lt(fi),fi.conflict=!1,fi.behind=!1,fi.error=null,fi.attempt=0}const[Ke,st]=await Promise.all([n.list(),n.trashed()]);s=new Set(st.map(fi=>fi.id)),f=new Set([...Ke,...st].map(fi=>fi.id));for(const[fi,Tt]of r)f.has(fi)||(lt(Tt),r.delete(fi));await Promise.all([...f].map(async fi=>{const Tt=X(fi);await Tt.loaded,Tt.busy===0&&await he(fi,Tt)}));const tn=t.session();if(tn===null){l=[],pe();return}const Cn=++G;try{l=await tn.api.list(),_=!1,x=null,A=0,N=Date.now()}catch(fi){const Tt=jve(fi);Tt==="retry"?(_=!0,A++,T!==null&&clearTimeout(T),T=setTimeout(()=>{T=null,qt.refresh(!0)},Math.min(dPt,uPt*2**(A-1)))):Tt==="rejected"&&(x=pPt(fi)),pe();return}m++,pe();const un=new Map(l.map(fi=>[fi.id,fi]));for(const fi of Ke)await wt(fi.id,!1,un.get(fi.id),Cn);for(const fi of st)await wt(fi.id,!0,un.get(fi.id),Cn);pe()}function Nt(Re,Ke){return Re.record!==null&&Re.record.wallet!==Ke?{state:"local",otherAccount:!0}:Re.conflict?Re.record===null?{state:"conflict",unrelated:!0}:{state:"conflict"}:Re.busy>0?{state:"syncing",progress:Re.progress}:Re.error!==null?{state:"error",message:Re.error}:Re.record!==null&&Re.pending.size===0&&!Re.behind?{state:"synced"}:{state:Re.retry!==null||_?"offline":"syncing"}}const Ut=()=>{for(const[Re,Ke]of r)Ke.retry!==null&&(clearTimeout(Ke.retry),Ke.retry=null,$e(Re));T!==null&&qt.refresh(!0)};typeof window<"u"&&window.addEventListener("online",Ut);const qt={status(){var Tn,fi,Tt;const Re=t.account(),Ke=t.session(),st={};for(const Wi of f){const Er=r.get(Wi);st[Wi]=Ke===null||Er===void 0?{state:"local"}:Nt(Er,Ke.wallet)}const tn=Object.entries(st).filter(([Wi])=>!s.has(Wi));let Cn="synced";Ke===null?Cn="local":x!==null?Cn="error":_&&(Cn="offline");for(const[,Wi]of tn)Wi.state!=="local"&&fPt.indexOf(Wi.state)<fPt.indexOf(Cn)&&(Cn=Wi.state);const un=((Tn=tn.find(([,Wi])=>Wi.state==="error"))==null?void 0:Tn[1].message)??x??void 0;return{account:Re,state:Cn,message:un,projects:st,revision:m,usage:((Tt=Ke==null?void 0:(fi=Ke.api).usage)==null?void 0:Tt.call(fi))??void 0}},subscribe(Re){return a.add(Re),()=>a.delete(Re)},remoteOnly(){return t.session()===null?[]:l.filter(Re=>Re.deletedAt==null&&!f.has(Re.id)).map(({id:Re,name:Ke,updatedAt:st})=>({id:Re,name:Ke,updatedAt:st}))},remoteIds:()=>l.map(Re=>Re.id),async manifest(Re){var st;const Ke=X(Re);return await Ke.loaded,Bve(n,t.hash,Re,((st=Ke.record)==null?void 0:st.base)??null,[...Ke.pending])},refresh(Re=!1){return Y?Promise.resolve():L!==null?L:!Re&&Date.now()-N<Lkr?Promise.resolve():(T!==null&&clearTimeout(T),T=null,L=fe(dt()).catch(Ke=>console.warn("[editor] could not compare the scenes with the account",Ke)).finally(()=>{L=null}),L)},touched(Re,Ke){if(!iJ(Ke))return;f.add(Re);const st=X(Re);st.error=null,st.loaded.then(()=>{if(xt(st))return;const tn=!st.pending.has(Ke);st.pending.add(Ke),tn&&st.record!==null&&Ee(Re,st),pe()}),t.session()!==null&&St(Re,C9e),pe()},async flush(Re){const Ke=[...r].filter(([st,tn])=>(Re===void 0||st===Re)&&tn.debounce!==null);await Promise.all(Ke.map(([st,tn])=>(tn.debounce!==null&&clearTimeout(tn.debounce),tn.debounce=null,$e(st))))},async prepareOpen(Re,Ke){const st=t.session();if(!f.has(Re)&&!(await n.list()).some(Tn=>Tn.id===Re)){if(st===null)throw new Error("This scene is not on this device.");let Tn,fi;try{if(Tn=await st.api.manifest(Re),Tn===null)throw new Error("This scene is no longer on your account.");fi=await lPt(st.api,Re,Object.entries(Tn.files),Ke??(()=>{}))}catch(Wi){throw Wi instanceof tO||jve(Wi)!=="retry"?Wi:new Error("This scene is on your account but not on this device yet, and you seem to be offline.")}await n.create(Tn.name,[...fi],{id:Re}),f.add(Re);const Tt=X(Re);await Tt.loaded,Tt.record={wallet:st.wallet,baseVersion:Tn.version,base:Tn.files,pending:[],remoteDeleted:!1},Tt.agreedAt=++G,await Ee(Re,Tt),m++,pe();return}if(f.add(Re),st===null)return;let tn=!1,Cn=!1;const un=qe(Re,async(Tn,fi)=>{if(tn||Tn.conflict)return;await he(Re,Tn);let Tt;try{Tt=await Uve(fi.api,Re)}catch{return}if(!tn){if(Cn=!0,Tt===null){Tn.record!==null&&(Tn.record=null,await Ee(Re,Tn)),St(Re,0);return}if(Tn.record===null||Tt.version!==Tn.record.baseVersion&&Tn.pending.size>0){(Tn.record===null||!await $t(Re,Tn,fi.wallet,Tt))&&(Tn.conflict=!0);return}if(Tt.version!==Tn.record.baseVersion)try{await Me(Re,Tn,fi,Tt,Tn.record.base,Ke)}catch(Wi){Wi instanceof Yxe||ht(Re,Tn,Wi,()=>{})}}});await Promise.race([un,new Promise(Tn=>setTimeout(()=>{Cn||(tn=!0,Tn())},Mkr))])},async resolve(Re,Ke){if(t.session()===null)throw new Error("Sign in to choose which version to keep.");const tn=Ke==="keep-both"?await t.duplicate(Re):null,Cn=await qe(Re,async(un,{api:Tn,wallet:fi})=>{const Tt=await Uve(Tn,Re),Wi=await Bve(n,t.hash,Re,null,[]);if(Ke==="keep-local"||Tt===null){await cPt(n,Tn,Re,Wi,(Tt==null?void 0:Tt.files)??{},Wt(un));const Er=await Tn.putManifest(Re,{name:await t.title(Re),files:Wi,baseVersion:(Tt==null?void 0:Tt.version)??null});un.record={wallet:fi,baseVersion:Er,base:Wi,pending:[],remoteDeleted:!1},un.agreedAt=++G,un.pending.clear(),un.conflict=!1,Ue(un),await Ee(Re,un)}else await Me(Re,un,{api:Tn,wallet:fi},Tt,Wi);return!0});if(tn!==null&&(f.add(tn),St(tn,0)),Cn!==!0)throw new Error("Sign in to choose which version to keep.");m++,pe()},trashed(Re){s.add(Re);const Ke=X(Re);lt(Ke),Ke.loaded.then(()=>{xt(Ke)&&(Ke.record=null,Ke.pending.clear(),Ee(Re,Ke))}),qe(Re,async(st,{api:tn})=>{if(Ke.record!==null)try{await tn.remove(Re),Ke.record.remoteDeleted=!0,await Ee(Re,Ke)}catch{}})},restored(Re){s.delete(Re);const Ke=X(Re);qe(Re,async(st,{api:tn})=>{if(Ke.record===null){St(Re,0);return}try{Ke.record.remoteDeleted&&await tn.restore(Re),Ke.record.remoteDeleted=!1,await Ee(Re,Ke),Ke.pending.size>0&&St(Re,0)}catch{}})},async idle(){for(;te.size>0;)await Promise.all([...te]);await Promise.all([...r.values()].map(Re=>Re.saving))},dispose(){Y=!0;for(const Re of r.values())lt(Re);T!==null&&clearTimeout(T),typeof window<"u"&&window.removeEventListener("online",Ut),a.clear()}};return qt}const uqe=67324752,yqt=33639248,dqe=101010256,Bkr=117853008,Ukr=2048,$kr=1,fqe=0,vqt=8,Xxe=4294967295;let $ve=null;function bqt(t){if($ve===null){$ve=new Uint32Array(256);for(let r=0;r<256;r++){let a=r;for(let s=0;s<8;s++)a=a&1?3988292384^a>>>1:a>>>1;$ve[r]=a}}let n=Xxe;for(const r of t)n=$ve[(n^r)&255]^n>>>8;return(n^Xxe)>>>0}async function xqt(t,n,r=1/0){const s=new Blob([t]).stream().pipeThrough(n).getReader(),l=[];let f=0;for(;;){const{done:x,value:T}=await s.read();if(x)break;if(f+=T.length,f>r)throw await s.cancel(),new Error("The zip file is damaged: an entry is larger than it says");l.push(T)}const m=new Uint8Array(f);let _=0;for(const x of l)m.set(x,_),_+=x.length;return m}function zkr(t){return{time:t.getHours()<<11|t.getMinutes()<<5|t.getSeconds()>>1,day:Math.max(1980,t.getFullYear())-1980<<9|t.getMonth()+1<<5|t.getDate()}}async function Vkr(t,n=new Date){const{time:r,day:a}=zkr(n),s=[],l=[];let f=0,m=0;for(const[L,B]of t){const G=new TextEncoder().encode(L),Y=await xqt(B,new CompressionStream("deflate-raw")),Z=Y.length>=B.length,te=Z?B:Y,pe=new DataView(new ArrayBuffer(26));pe.setUint16(0,20,!0),pe.setUint16(2,Ukr,!0),pe.setUint16(4,Z?fqe:vqt,!0),pe.setUint16(6,r,!0),pe.setUint16(8,a,!0),pe.setUint32(10,bqt(B),!0),pe.setUint32(14,te.length,!0),pe.setUint32(18,B.length,!0),pe.setUint16(22,G.length,!0);const fe=new Uint8Array(30+G.length);new DataView(fe.buffer).setUint32(0,uqe,!0),fe.set(new Uint8Array(pe.buffer),4),fe.set(G,30);const X=new Uint8Array(46+G.length),he=new DataView(X.buffer);if(he.setUint32(0,yqt,!0),he.setUint16(4,788,!0),X.set(new Uint8Array(pe.buffer),6),he.setUint32(38,33188<<16>>>0,!0),he.setUint32(42,f,!0),X.set(G,46),s.push(fe,te),l.push(X),f+=fe.length+te.length,m++,f>Xxe||m>65535)throw new Error("The project is too large for a .zip file (4 GB, 65535 files)")}const _=l.reduce((L,B)=>L+B.length,0),x=new Uint8Array(22),T=new DataView(x.buffer);T.setUint32(0,dqe,!0),T.setUint16(8,m,!0),T.setUint16(10,m,!0),T.setUint32(12,_,!0),T.setUint32(16,f,!0);const A=new Uint8Array(f+_+x.length);let N=0;for(const L of[...s,...l,x])A.set(L,N),N+=L.length;return A}function Wkr(t){const n=new DataView(t.buffer,t.byteOffset,t.byteLength);let r=t.length-22;for(;r>=0&&r>=t.length-22-65535&&n.getUint32(r,!0)!==dqe;)r--;if(r<0||n.getUint32(r,!0)!==dqe)throw new Error("This is not a .zip file");const a=n.getUint16(r+10,!0);let s=n.getUint32(r+16,!0);if(a===65535||s===Xxe||r>=20&&n.getUint32(r-20,!0)===Bkr)throw new Error("This .zip file is too large to import (zip64)");const l=[],f=[];for(let m=0;m<a;m++){if(s+46>t.length||n.getUint32(s,!0)!==yqt)throw new Error("The zip file is damaged");const _=n.getUint16(s+8,!0),x=n.getUint16(s+10,!0),T=n.getUint32(s+16,!0),A=n.getUint32(s+20,!0),N=n.getUint32(s+24,!0),L=n.getUint16(s+28,!0),B=n.getUint32(s+42,!0),G=new TextDecoder().decode(t.subarray(s+46,s+46+L));if(s+=46+L+n.getUint16(s+30,!0)+n.getUint16(s+32,!0),G.endsWith("/"))continue;if(B+30>t.length||n.getUint32(B,!0)!==uqe)throw new Error("The zip file is damaged");const Y=B+30+n.getUint16(B+26,!0)+n.getUint16(B+28,!0)+A;if(Y>t.length)throw new Error("The zip file is damaged");f.push([B,Y]),l.push({path:G,size:N,async read(){if(_&$kr)throw new Error(`"${G}" is encrypted`);if(x!==fqe&&x!==vqt)throw new Error(`"${G}" uses a compression this editor cannot read`);if(B+30>t.length||n.getUint32(B,!0)!==uqe)throw new Error("The zip file is damaged");const Z=B+30+n.getUint16(B+26,!0)+n.getUint16(B+28,!0),te=t.subarray(Z,Z+A),pe=x===fqe?te.slice():await xqt(te,new DecompressionStream("deflate-raw"),N);if(pe.length!==N||bqt(pe)!==T)throw new Error(`The zip file is damaged: "${G}" does not check out`);return pe}})}f.sort(([m],[_])=>m-_);for(let m=1;m<f.length;m++)if(f[m][0]<f[m-1][1])throw new Error("The zip file is damaged: two of its files share the same bytes");return l}const hM={files:2e3,fileBytes:50*1024*1024,totalBytes:500*1024*1024},hPt=1024*1024,qkr=/(^|\/)(__MACOSX\/|\.DS_Store$|Thumbs\.db$|desktop\.ini$)/,wqt=new Set(["node_modules",".git"]),zve=t=>t.split("/").length;async function Gkr(t,n){var te,pe;const r=n.map(fe=>({entry:fe,path:fe.path.replace(/\\/g,"/").replace(/^(\.\/)+/,"")})).filter(({path:fe})=>!qkr.test(fe));if(r.some(({path:fe})=>fe==="dcl-workspace.json"))return{ok:!1,reason:"This is a workspace of several scenes. Import one scene folder at a time."};const a=r.map(({path:fe})=>fe).filter(fe=>(fe==="scene.json"||fe.endsWith("/scene.json"))&&!fe.split("/").some(X=>wqt.has(X))).sort((fe,X)=>zve(fe)-zve(X));if(a.length===0)return{ok:!1,reason:"There is no scene.json here, so this does not look like a Decentraland scene."};if(a.length>1&&zve(a[0])===zve(a[1]))return{ok:!1,reason:"There are several scenes here. Import one scene folder at a time."};const s=a[0].slice(0,-10),l=r.filter(({path:fe})=>fe.startsWith(s)).map(({entry:fe,path:X})=>({entry:fe,path:X.slice(s.length)})),f=l.filter(({path:fe})=>iJ(fe)),m=fe=>({ok:!1,reason:`This scene is too large to import: ${fe}.`}),_=fe=>m(`${fe} is larger than ${hM.fileBytes/hPt} MB`),x=()=>m(`its files add up to more than ${hM.totalBytes/hPt} MB`);if(f.length>hM.files)return m(`it has more than ${hM.files} files`);const T=f.find(({entry:fe})=>(fe.size??0)>hM.fileBytes);if(T!==void 0)return _(T.path);if(f.reduce((fe,{entry:X})=>fe+(X.size??0),0)>hM.totalBytes)return x();const A=new Map;let N=0;for(const{entry:fe,path:X}of f){const he=await fe.read();if(he.length>hM.fileBytes)return _(X);if(N+=he.length,N>hM.totalBytes)return x();A.set(X,he)}const L=l.length-f.length,B=N0(A.get("scene.json"));if(B===null)return{ok:!1,reason:"scene.json is not valid JSON."};if(typeof((te=B.scene)==null?void 0:te.base)!="string"||!Array.isArray(B.scene.parcels))return{ok:!1,reason:"scene.json does not say which parcels the scene is on (scene.base and scene.parcels)."};const G=[];B.runtimeVersion!=="7"&&G.push('This is not an SDK7 scene (scene.json has no runtimeVersion "7"). The editor builds SDK7 scenes only, so it will most likely not run.'),[...A.keys()].some(fe=>fe.startsWith("server/"))&&G.push("This project has a server/ folder. The web editor does not run or publish a scene server: the files are kept, and nothing in them runs.");const Y=dqt(A.get("package.json"));return{ok:!0,plan:{title:typeof((pe=B.display)==null?void 0:pe.title)=="string"&&B.display.title.trim()!==""?B.display.title.trim():t,files:A,skipped:L,warnings:G,needsPackages:Y}}}function Hkr(t){return Wkr(t)}async function Sqt(t,n=""){const r=[];for await(const[a,s]of t.entries())s.kind==="file"?r.push({path:n+a,read:async()=>new Uint8Array(await(await s.getFile()).arrayBuffer())}):wqt.has(a)||r.push(...await Sqt(s,`${n}${a}/`));return r}function Tqt(){const t=window.showDirectoryPicker;return typeof t=="function"?t.bind(window):null}const Jkr=t=>t instanceof Error?t.message:String(t);function kqt(t){return new Promise(n=>{const r=document.createElement("input");r.type="file",r.accept=t,r.onchange=()=>{var a;return n(((a=r.files)==null?void 0:a[0])??null)},r.oncancel=()=>n(null),r.click()})}const Qkr=()=>Tqt()!==null;async function Ykr(t,n){const r=[...n].sort(([f],[m])=>f.localeCompare(m)),a=await Vkr(r),s=URL.createObjectURL(new Blob([a],{type:"application/zip"})),l=document.createElement("a");l.href=s,l.download=`${t.replace(/[^\w.-]+/g,"-").replace(/^-+|-+$/g,"")||"scene"}.zip`,l.click(),setTimeout(()=>URL.revokeObjectURL(s),6e4)}async function Xkr(t){if(t==="zip"){const r=await kqt(".zip,application/zip");return r===null?null:{name:r.name.replace(/\.zip$/i,""),entries:Hkr(new Uint8Array(await r.arrayBuffer()))}}const n=Tqt();if(n===null)throw new Error("This browser cannot open a folder. Zip the scene folder and import the .zip.");try{const r=await n({mode:"read"});return{name:r.name,entries:await Sqt(r)}}catch(r){if(r instanceof Error&&r.name==="AbortError")return null;throw r}}async function Zkr(t,n){try{const r=await Xkr(t);if(r===null)return null;const a=await Gkr(r.name,r.entries);if(!a.ok)return a;const{title:s,files:l,skipped:f,warnings:m,needsPackages:_}=a.plan;return{ok:!0,report:{id:await n(s,l),title:s,files:l.size,skipped:f,warnings:m,needsPackages:_}}}catch(r){return{ok:!1,reason:Jkr(r)}}}function Kkr(t,n,r){let a=null;const s=new Map,l=()=>(a??(a=r(new URL("prefabs/catalog.json",t).href,n,"prefabs/catalog.json").then(async f=>await f.json()).catch(f=>{throw a=null,f})),a);return{async folders(){return(await l()).map(f=>({name:f.name,data:f.data,files:Object.fromEntries(Object.entries(f.files).map(([m,_])=>[m,_.size]))}))},async read(f,m){var A;const _=(A=(await l()).find(N=>N.name===f.name))==null?void 0:A.files[m];if(_===void 0)throw new Error(`${f.name}/${m} is not a built-in prefab file`);const x=`prefabs/${f.name}/${m}`;let T=s.get(x);if(T===void 0){const N=new URL(x.split("/").map(encodeURIComponent).join("/"),t).href;T=r(N,_.integrity,x).then(async L=>new Uint8Array(await L.arrayBuffer())),s.set(x,T),T.catch(()=>s.delete(x))}return T}}}const e2r=`{
34595
34595
  "version": 1,
34596
34596
  "components": [
@@ -37690,4 +37690,4 @@ ${n}
37690
37690
  ${r}`));return"w"+Array.from(new Uint8Array(a).slice(0,16),s=>s.toString(16).padStart(2,"0")).join("")}function J2r(t){return`${t.file===void 0?"":`${t.file}:${t.line??1}:${t.column??0}: `}${t.severity==="error"?"ERROR":"WARNING"}: ${t.message}`}function Q2r(t){return t.length<=3?t.join(", "):`${t.slice(0,3).join(", ")} and ${t.length-3} more`}function Y2r(t,n){const r=N0(t.get("package.json"));for(const a of[r==null?void 0:r.devDependencies,r==null?void 0:r.dependencies]){const s=typeof a=="object"&&a!==null?a["@dcl/sdk"]:void 0;if(typeof s=="string"&&s!==n)return`⚠ This project asks for @dcl/sdk ${s}; the editor builds it with ${n}.`}return null}function X2r(t){const{emit:n,log:r}=t;let a=null,s=!1,l=null,f=Promise.resolve();const m=A=>{const N=f.then(A,A);return f=N.catch(()=>{}),N};async function _(){if(s)throw new Error("The editor was closed");a===null&&(l=null);const A=a??bSr(t.urls);a=A;try{const N=await A.ready;return{builder:A,sdkVersion:N.sdkVersion,info:N}}catch(N){throw A.dispose(),a===A&&(a=null),N}}async function x(A,N,L){const{builder:B,info:G}=await _();let Y=!1;const Z=await q2r(N,t.packages,G.snapshot,pe=>{t.onPackages(A,pe),L&&!Y&&r("Downloading packages from npm…"),Y=!0});Z.lockFile!==void 0&&(N.set(FR,Z.lockFile),await t.saveLock(A,Z.lockFile));const te=Z.lock===null?"":eqt(Z.lock);if(te!==l)try{await B.setPackages(Z.lock,t.packages),l=te}catch(pe){L&&r(`⚠ ${vPt(pe)}`),await B.setPackages(null),l=""}if(t.onPackages(A,Z.status),!(!L||Z.status===null)){if(Z.status.state==="error")r(`✖ ${Z.status.message??"The packages could not be installed"}`);else if(Z.downloaded>0){const pe=Object.entries(Z.status.installed).map(([fe,X])=>`${fe} ${X}`);r(`✔ Packages frozen into the scene: ${pe.join(", ")} (${Z.status.total} in all)`)}for(const pe of Z.status.warnings)r(`⚠ ${pe}`)}}function T(A,N,L=A){const B=tqe(t.pageDir,L),G=new Set;let Y=null,Z=null,te=!0,pe=!1,fe=!1,X=null;const he=xt=>(n({type:"failed",message:xt}),r(`✖ Build failed: ${xt.split(`
37691
37691
  `)[0]}`),!1);async function Ee(xt){const lt=performance.now();n({type:"buildStarted",changed:xt}),r(xt.length===0?"Building the scene…":`Rebuilding: ${Q2r(xt)}`);try{const{builder:St,sdkVersion:ht}=await _(),Ue=xt.length===0?Y2r(N,ht):null;Ue!==null&&r(Ue);const Wt=xt.length===0||xt.includes("package.json")||xt.includes(FR),{snapshot:$e,result:Me}=await m(async()=>{(Wt||l!==X)&&(await x(A,N,!0),X=l);const wt=new Map(N);return{snapshot:wt,result:await St.build(wt)}});if(fe)return!1;const $t=Me.diagnostics.filter(wt=>wt.severity==="error"||wt.file===void 0||$e.has(wt.file));n({type:"diagnostics",diagnostics:$t.map(({severity:wt,message:dt,file:Nt,line:Ut,column:qt})=>({severity:wt,message:dt,file:Nt,line:Ut,column:qt}))});for(const wt of $t)r(J2r(wt));if(!Me.ok){const wt=Me.events.find(dt=>dt.type==="failed");return he((wt==null?void 0:wt.type)==="failed"?wt.message:"The build produced no bundle")}const vt=N0($e.get("scene.json"));return vt===null?he("scene.json is missing or is not valid JSON"):(await PTr({realmUrl:B,projectId:L,files:$e,buildOutputs:Me.outputs,sceneJson:vt,sdkVersion:ht,machineId:await t.machineId(L)}),fe?!1:(n({type:"bundleSaved"}),r(`✔ Built in ${Math.round(performance.now()-lt)} ms`),!0))}catch(St){return fe?!1:he(vPt(St))}}function qe(){return fe?Promise.resolve(!1):(Z!==null||(Z=(async()=>{for(;(te||G.size>0)&&!fe;){const xt=te?[]:[...G];te=!1,G.clear(),pe=await Ee(xt)}return pe&&!fe})().finally(()=>{Z=null})),Z)}return{id:A,files:N,start:qe,flush(){return Y!==null&&clearTimeout(Y),Y=null,qe()},async published(){const xt=await(await caches.open(Qxe)).match(nqe(B)),lt=await(xt==null?void 0:xt.json().catch(()=>{})),St=lt==null?void 0:lt.entity;return(St==null?void 0:St.id)===_Ze(L,await t.machineId(L))},changed(xt){fe||(G.add(xt),Y!==null&&clearTimeout(Y),Y=setTimeout(()=>{Y=null,qe()},G2r))},stop(){fe=!0,Y!==null&&clearTimeout(Y)}}}return{realmUrl:A=>tqe(t.pageDir,A),open:T,async build(A,N,L){const{builder:B,sdkVersion:G}=await _();return m(async()=>(await x(A,N,!1),{...await B.build(N,L),sdkVersion:G}))},install:(A,N)=>m(()=>x(A,N,!1)),dispose(){s=!0,a==null||a.dispose(),a=null}}}const xPt=64*1024*1024,Z2r=4*1024*1024;function K2r(t,n){return t.length>8192?!1:n==="POST"?t==="/content/entities/active":n!=="GET"&&n!=="HEAD"?!1:t==="/about"||t==="/scene.json"||/^\/content\/contents\/(?:b64-[A-Za-z0-9+/=]+|[A-Za-z0-9]+)$/.test(decodeURIComponent(t))}function eEr(t,n,r){const a=t===null?null:new URL(t).origin;let s="",l=null,f="",m="",_=new AbortController,x=0,T=0;const A=(B,G=[])=>{l!==null&&a!==null&&l.postMessage({...B,session:n,instance:s},a,G)},N=async B=>{var he;if(typeof B.requestId!="string"||B.requestId.length>128||typeof B.path!="string")return;const G=B.requestId;if(x>=64){A({type:"realm-response",requestId:G,error:"Too many preview resource requests"});return}const Y=_,Z=l,te=new AbortController,pe=setTimeout(()=>te.abort(new Error("Preview resource timed out")),3e4);let fe=0,X;x++;try{const Ee=new URL(f+B.path);if(!Ee.href.startsWith(f+"/")||!K2r(B.path,B.method))throw new Error("Invalid preview resource");if(B.body!==void 0&&(!(B.body instanceof ArrayBuffer)||B.body.byteLength>1024*1024))throw new Error("Invalid preview request body");const qe=await fetch(Ee,{method:String(B.method),credentials:"omit",redirect:"error",signal:AbortSignal.any([Y.signal,te.signal]),headers:{"Content-Type":"application/json"},body:B.method==="POST"?B.body:void 0}),xt=qe.headers.get("Content-Type")??"application/octet-stream",lt=xt.includes("application/json")?Z2r:xPt;if(X=(he=qe.body)==null?void 0:he.getReader(),Number(qe.headers.get("Content-Length"))>lt)throw new Error("Preview resource exceeds the memory limit");const St=[];if(X!==void 0)for(;;){const{done:$e,value:Me}=await X.read();if($e)break;if(fe+=Me.byteLength,T+=Me.byteLength,fe>lt||T>xPt)throw new Error("Preview resource exceeds the memory limit");St.push(Me)}const ht=new Uint8Array(fe);let Ue=0;for(const $e of St)ht.set($e,Ue),Ue+=$e.byteLength;St.length=0;let Wt=ht.buffer;if(B.method!=="HEAD"&&Wt.byteLength>0&&xt.includes("application/json")){const $e=JSON.parse(new TextDecoder().decode(Wt)),Me=$t=>typeof $t=="string"?$t.startsWith(f+"/")||$t===f?m+$t.slice(f.length):$t:Array.isArray($t)?$t.map(Me):vc($t)?Object.fromEntries(Object.entries($t).map(([vt,wt])=>[vt,Me(wt)])):$t;Wt=new TextEncoder().encode(JSON.stringify(Me($e))).buffer}!Y.signal.aborted&&Z===l&&A({type:"realm-response",requestId:G,status:qe.status,headers:{"Content-Type":xt},body:Wt},[Wt])}catch(Ee){!Y.signal.aborted&&Z===l&&A({type:"realm-response",requestId:G,error:Ee instanceof Error?Ee.message:String(Ee)})}finally{clearTimeout(pe),te.abort(),X!==void 0&&X.cancel().catch(Ee=>{Ee instanceof Error&&Ee.name==="AbortError"||console.warn("Preview stream cleanup:",Ee)}),T-=fe,x--}},L=B=>{const G=B.data;B.origin!==a||!vc(G)||G.session!==n||G.instance!==s||s===""||(G.type==="connect"&&l===null&&B.source!==null&&"postMessage"in B.source&&(l=B.source),!(B.source!==l||l===null)&&(G.type==="realm-request"?N(G):r(G)))};return window.addEventListener("message",L),{send:A,reset:(B="",G="",Y="")=>{_.abort(),_=new AbortController,l=null,s=B,f=G,m=Y},dispose:()=>{_.abort(),window.removeEventListener("message",L)}}}const wPt=["localhost","127.0.0.1","[::1]"];function tEr(t,n,r=""){if(wPt.includes(new URL(t).hostname)){const a=new URLSearchParams(r).get("editor-mobile");return`${a!==null&&URL.canParse(a)&&wPt.includes(new URL(a).hostname)?new URL(a).origin:"http://127.0.0.1:8788"}/mobile-preview/index.html`}return n===null?null:new URL("mobile-preview/index.html",n.replace(/\/$/,"")+"/").href}function nEr(t){if(t===null)return null;const n=new URL(t).hostname;return n==="decentraland.zone"||n.endsWith(".decentraland.zone")?"zone":null}function rEr(t,n,r,a=null){const s=crypto.randomUUID(),l=eEr(t,s,fe=>pe(fe)),f=new Set,m=new Map;let _={mode:"closed",url:null,ready:!1},x=null,T="",A="",N=0,L=null,B=!1,G=null;const Y=fe=>{_=fe;for(const X of f)X()},Z=()=>{A!==""&&l.send({type:"stop",session:s,instance:A}),l.reset(),G==null||G.close(),G=null,A="";for(const fe of m.values())fe.reject(new Error("The mobile preview was closed."));m.clear(),Y({mode:"closed",url:null,ready:!1})},te=()=>{N++,x=null,Z()};window.addEventListener("pagehide",te);const pe=fe=>{if(!vc(fe)||fe.session!==s||fe.instance!==A||(fe.type==="ready"&&(Y({..._,ready:!0}),l.send({type:"visibility",session:s,instance:A,visible:_.mode!=="minimized"})),fe.type==="closed"&&te(),fe.type!=="capture"||typeof fe.requestId!="string"))return;const X=m.get(fe.requestId);X!==void 0&&(typeof fe.error=="string"?X.reject(new Error(fe.error)):typeof fe.image=="string"&&fe.image.length<=4e6&&typeof fe.width=="number"&&fe.width>0&&fe.width<=1024&&typeof fe.height=="number"&&fe.height>0&&fe.height<=1024?X.resolve({mimeType:"image/jpeg",image:fe.image,width:fe.width,height:fe.height}):X.reject(new Error("The mobile preview sent an invalid picture.")))};return{state:()=>_,status:()=>({mode:_.mode,ready:_.ready}),subscribe:fe=>(f.add(fe),()=>f.delete(fe)),open:async(fe,X,he="embedded")=>{if(B||t===null)return{ok:!1,reason:"unavailable"};const Ee=fe.replace(/\/$/,"");if(_.mode===he&&x===Ee)return N++,{ok:!0};if((he==="embedded"||he==="minimized")&&(_.mode==="embedded"||_.mode==="minimized")&&x===Ee){if(N++,Y({..._,mode:he}),l.send({type:"visibility",session:s,instance:A,visible:he!=="minimized"}),he==="minimized")for(const Wt of m.values())Wt.reject(new Error("Restore the mobile preview before taking a picture."));return{ok:!0}}if(he==="minimized")return{ok:!1,reason:"unavailable"};const qe=he==="tab"?window.open("about:blank",`dcl-phone-preview-${crypto.randomUUID()}`):null;if(he==="tab"&&qe===null)return{ok:!1,reason:"unavailable"};const xt=++N;if(!await fetch(t,{method:"HEAD",signal:AbortSignal.timeout(5e3)}).then(Wt=>Wt.ok,()=>!1)||xt!==N||B||qe!=null&&qe.closed)return qe==null||qe.close(),{ok:!1,reason:"unavailable"};if(Z(),await new Promise(Wt=>setTimeout(Wt,100)),xt!==N||B||qe!=null&&qe.closed)return qe==null||qe.close(),{ok:!1,reason:"unavailable"};A=crypto.randomUUID();const St=new URL(t);St.searchParams.set("previewSession",s),St.searchParams.set("previewInstance",A),St.searchParams.set("editorOrigin",window.location.origin),he==="tab"&&St.searchParams.set("previewDetached","1"),St.searchParams.set("maxpixels",String(1600*720)),T=new URL(`realm/${s}/${A}`,t).href,l.reset(A,Ee,T);const ht=new URL("decentraland://open");ht.searchParams.set("preview",T),ht.searchParams.set("position",X);const Ue=["--local-editor-preview","--skip-lobby","--guest-profile","--fake-deeplink",ht.href];return a!==null&&Ue.push("--dclenv",a),St.searchParams.set("args",Ue.join(" ")),x=Ee,Y({mode:he,url:St.href,ready:!1}),G=qe,qe!==null&&iEr(qe,St.href),{ok:!0}},close:te,capture:async fe=>{if(fe.throwIfAborted(),_.mode==="closed"||!_.ready)throw new Error("Open the mobile preview and wait for it to load before taking a picture.");_.mode==="minimized"&&(L=N,Y({..._,mode:"embedded"}),l.send({type:"visibility",session:s,instance:A,visible:!0}));const X=crypto.randomUUID();try{return await new Promise((he,Ee)=>{const qe=()=>{clearTimeout(lt),fe.removeEventListener("abort",xt),m.delete(X)},xt=()=>{qe(),Ee(fe.reason)},lt=setTimeout(()=>{qe(),Ee(new Error("The mobile preview did not answer. Bring its tab to the foreground and retry."))},1e4);m.set(X,{resolve:St=>{qe(),he(St)},reject:St=>{qe(),Ee(St)}}),fe.addEventListener("abort",xt,{once:!0}),l.send({type:"capture",session:s,instance:A,requestId:X})})}finally{m.size===0&&(L===N&&_.mode==="embedded"&&(Y({..._,mode:"minimized"}),l.send({type:"visibility",session:s,instance:A,visible:!1})),L=null)}},built:async fe=>{if(x===null)return;const X=x,he=await n(fe),Ee=_Ze(he,await r(he));!B&&X===x&&l.send({type:"scene-updated",session:s,instance:A,realm:T,entityId:Ee})},dispose:()=>{te(),B=!0,window.removeEventListener("pagehide",te),l.dispose(),f.clear()}}}function iEr(t,n){const r=t.document;r.title="Mobile scene preview",r.body.style.cssText="margin:0;background:#19191c;overflow:hidden";const a=r.createElement("iframe");a.title="Mobile scene preview",a.allow="autoplay; fullscreen; gamepad; cross-origin-isolated",a.style.cssText="width:100vw;height:100vh;border:0;display:block",a.src=n,r.body.replaceChildren(a)}const aEr=200,sEr=2e3,oEr=/^\((?:no logs|\+\d+ older entries not retained)\)$/;function cEr(t,n){for(let r=Math.min(t.length,n.length);r>0;r--){const a=t.length-r;if(n.slice(0,r).every((s,l)=>s===t[a+l]))return n.slice(r)}return[...n]}function lEr(t,n){let r=[],a=!1,s;const l=async()=>{try{const f=await t(`scene_logs ${aEr} --server`);if(a)return;const m=f.split(`
37692
37692
  `).filter(_=>_.trim()!==""&&!oEr.test(_.trim()));for(const _ of cEr(r,m))n(_);r=m}catch{}a||(s=setTimeout(()=>void l(),sEr))};return l(),()=>{a=!0,clearTimeout(s)}}function uEr(t,n,r){r.throwIfAborted();const a=nt.scene,s=[...t].sort().join(";");return new Promise((l,f)=>{const m=()=>{clearTimeout(N),T(),r.removeEventListener("abort",A)},_=L=>{m(),f(L)},x=()=>{var B;nt.scene===a||nt.status!=="ready"||!nt.frozen||((B=nt.scene)==null?void 0:B.parcels.map(G=>`${G.x},${G.y}`).sort().join(";"))!==s||(m(),l())},T=yqe(x),A=()=>_(r.reason),N=setTimeout(()=>_(new Error("The preview has not loaded the new parcels. Retry sceneSettings before building outside the current layout.")),9e4);r.addEventListener("abort",A,{once:!0});try{n(),x()}catch(L){_(L)}})}const dEr=new Set(["node_modules",".git","bin","dist"]),fEr=300*1024,pEr=56,mEr=5;function hEr(t){const n=t.origin,r=typeof n=="object"&&n!==null&&!Array.isArray(n)?n:null;return r!==null&&(r.source==="import"||r.source==="github")?r:{...r??{},source:"user"}}function _Er(t,n){const r=new Map;let a=new Map;function s(m,_){if(_.size>fEr)return t.blob(m,_.hash);let x=r.get(_.hash);return x===void 0&&(x=t.blob(m,_.hash),r.set(_.hash,x),x.catch(()=>r.delete(_.hash))),x}const l={async folders(){const m=await t.list();return a=new Map(m.map(x=>[x.name,x])),(await Promise.all(m.map(async x=>{const T=x.files["data.json"];if(T===void 0)return null;const A=await s(x.name,T).then(L=>new TextDecoder().decode(L),()=>null);if(A===null)return null;const N=Object.fromEntries(Object.entries(x.files).map(([L,B])=>[L,B.size]));return{name:x.name,data:A,files:N}}))).filter(x=>x!==null)},async read(m,_){var T;const x=(T=a.get(m.name))==null?void 0:T.files[_];if(x===void 0)throw new Error(`${m.name}/${_} is not in your library`);return s(m.name,x)}};async function f(m,_){const x={},T=new Map;for(const[N,L]of _){const B=await n(L);x[N]={hash:B,size:L.length},T.set(B,L)}const A=[...T].map(([N,L])=>({hash:N,size:L.length}));for(const N of await t.uploads(m,A)){const L=T.get(N.hash);L!==void 0&&await t.putBlob(N,L)}await t.put(m,x)}return{tree:l,async copyOut(m,_){const[x,T,A]=_.split("/");if(x!=="custom"||T===void 0||A!==void 0||!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(T))throw new Error(`not a prefab folder: ${_}`);const N=`${_}/`,L=new Map;for(const[X,he]of m.files()){const Ee=X.slice(N.length);X.startsWith(N)&&!Ee.split("/").some(qe=>dEr.has(qe))&&L.set(Ee,he)}const B=L.get("data.json"),G=B===void 0?null:Vq(new TextDecoder().decode(B));if(G===null)throw new Error(`${_} has no readable data.json`);const Y={...G,id:typeof G.id=="string"?G.id:crypto.randomUUID(),origin:hEr(G)},Z=`${JSON.stringify(Y,null,2)}
37693
- `;L.set("data.json",new TextEncoder().encode(Z));const te=pqe(Cqt(G,T)).slice(0,pEr).replace(/_+$/,"")||"prefab",pe=new Set((await t.list()).map(X=>X.name));let fe=1;for(let X=0;X<mEr;X++){let he=te;for(;pe.has(he);)he=`${te}_${++fe}`;try{return await f(he,L),{ref:`user:${he}`,scope:"user",data:Z}}catch(Ee){if(!(Ee instanceof _qt))throw Ee;pe.add(he)}}throw new Error("could not find a free name in your library — try again")},remove:m=>t.remove(m)}}const SPt=new WeakMap;function gEr(t){let n=SPt.get(t);return n===void 0&&(n=crypto.subtle.digest("SHA-256",new Uint8Array(t)).then(r=>[...new Uint8Array(r)].map(a=>a.toString(16).padStart(2,"0")).join("")),SPt.set(t,n)),n}async function yEr(t){const n=t.get(".dclignore"),r=yZe(t.keys(),n===void 0?void 0:new TextDecoder().decode(n)).filter(l=>l!==FR),a=new Map;let s=0;for(const l of r){const f=t.get(l);f!==void 0&&(a.set(await gEr(f),f.byteLength),s++)}return{bytes:[...a.values()].reduce((l,f)=>l+f,0),files:s}}const TPt=4e3,vEr=4e6,bEr=UPt*24*60*60*1e3,xEr="assets/images",wEr=3e3,SEr=100,TEr="Sign in to keep prefabs in your own library. The built-in prefabs work without it.",kEr=t=>t instanceof Error?t.message:String(t);function O9e(t,n){var r,a,s;return{realm:n.replace(/\/$/,""),systemScene:"",position:((r=t.scene)==null?void 0:r.base)??((s=(a=t.scene)==null?void 0:a.parcels)==null?void 0:s[0])??"0,0",spawn:qTr(t),spawnPoints:JSON.stringify(Array.isArray(t.spawnPoints)?t.spawnPoints:[])}}const EEr=({host:t,sceneCompiler:n})=>{const r=new Ikr(kkr()),a=Ok(t),s=a===null||a.services.projects===null?null:hqt(a.services.projects,(Xn,Lr)=>a.signedFetch(Xn,Lr)),l=()=>{const{address:Xn,isGuest:Lr}=t.identity();return Lr||Xn===null?null:Xn.toLowerCase()},f=[],m=[],_=[],x=[],T=[],A=Xn=>{f.push(Xn),f.length>TPt&&f.splice(0,f.length-TPt);for(const Lr of m)Lr(Xn)},N=rEr(tEr(t.pageDir,(a==null?void 0:a.services.projects)??null,location.search),he,async Xn=>bPt(await Y,l()??"guest",Xn),nEr((a==null?void 0:a.services.worldsContent)??null)),L=Xn=>{for(const Lr of _)Lr(Xn);Xn.type==="bundleSaved"&&pe!==null&&N.built(pe.id).catch(Lr=>console.error("[editor] mobile preview reload failed",Lr))},B=new Map,G=new Set,Y=I3t.then(H2r),Z=X2r({pageDir:t.pageDir,urls:n,machineId:async Xn=>bPt(await Y,l()??"guest",Xn),emit:L,log:A,packages:r.packages,saveLock:async(Xn,Lr)=>{await r.write(Xn,FR,Lr),wt.touched(Xn,FR),(pe==null?void 0:pe.id)===Xn&&pe.files.set(FR,Lr)},onPackages:(Xn,Lr)=>{if(Lr===null){B.delete(Xn);return}B.set(Xn,Lr);for(const ti of G)ti(Xn,Lr)}});let te=null,pe=null,fe=null,X=null;async function he(Xn){var Ga;const Lr=(Ga=(await r.list()).find(Za=>Za.id===Xn))==null?void 0:Ga.previewId;if(Lr!==void 0)return Lr;const ti="p"+Array.from(crypto.getRandomValues(new Uint8Array(16)),Za=>Za.toString(16).padStart(2,"0")).join("");return await r.patch(Xn,{previewId:ti}),ti}async function Ee(Xn){const Lr=new URL("preview/",t.pageDir).href,ti=await caches.open(Qxe);for(const Ga of await ti.keys())Ga.url.startsWith(Lr)&&(Xn===null||!Ga.url.startsWith(Xn))&&await ti.delete(Ga)}let qe=0,xt=Promise.resolve(),lt=Promise.resolve();const St=RTr({service:(a==null?void 0:a.services.projects)??null,fetch:(Xn,Lr)=>a===null?Promise.reject(new Error("The assistant service is not configured.")):a.signedFetch(Xn,Lr),wallet:l,mobilePreview:N.status,memory:{read:async(Xn,Lr)=>{const ti=await r.assistantMemory(Xn,Lr);return vc(ti)&&typeof ti.id=="string"&&typeof ti.key=="string"?{id:ti.id,key:ti.key}:null},write:(Xn,Lr,ti)=>r.setAssistantMemory(Xn,Lr,ti)},project:()=>{const Xn=Tt();return{id:Xn.id,tools:{files:()=>Xn.files,write:(Lr,ti,Ga,Za)=>Wt(Xn.id,Lr,ti,{expected:Ga,signal:Za}),assets:czt({files:()=>Xn.files}),scene:(Lr,ti)=>ATe(Lr,ti,async Ga=>{var ii,yl,ja;const Za=ss=>{var Ts,Zs;return JSON.stringify([(Ts=ss==null?void 0:ss.scene)==null?void 0:Ts.parcels,(Zs=ss==null?void 0:ss.scene)==null?void 0:Zs.base])};await WE();const el=Za(N0(await Ue(Xn.id,"scene.json")));await tn(Xn.id,ss=>QTr(ss,Ga));const uu=N0(await Ue(Xn.id,"scene.json")),lr=Za(uu)!==el,Pl=((ii=uu==null?void 0:uu.scene)==null?void 0:ii.parcels)??[],fm=(yl=nt.scene)==null?void 0:yl.parcels.map(ss=>`${ss.x},${ss.y}`).sort().join(";");if(lr||fm!==[...Pl].sort().join(";")){if(!await Xn.flush())throw new Error("The scene did not build. Fix the code and retry sceneSettings to load the new parcels.");ti.throwIfAborted();const ss=await he(Xn.id);te=O9e(uu??{},Z.realmUrl(ss)),await uEr(Pl,()=>{for(const Ts of x)Ts(te)},ti)}else if(te!==null){const ss=O9e(uu??{},te.realm);(ss.spawn!==te.spawn||ss.spawnPoints!==te.spawnPoints)&&(te={...te,spawn:ss.spawn,spawnPoints:ss.spawnPoints},qdr(ss.spawn,ss.spawnPoints))}return{requiredPermissions:(uu==null?void 0:uu.requiredPermissions)??[],spawnPoints:(uu==null?void 0:uu.spawnPoints)??[],parcels:Pl,base:((ja=uu==null?void 0:uu.scene)==null?void 0:ja.base)??null,previewReady:!0}}),written:dzt,view:(Lr,ti)=>CTe(Lr,ti),playtest:YXe,uiLayout:PWe,turnEnded:oVt,build:async Lr=>{Lr.throwIfAborted(),await lt;const ti=await Z.build(Xn.id,new Map(Xn.files));if(Lr.throwIfAborted(),!ti.ok)return{ok:!1,diagnostics:ti.diagnostics,mobilePreview:N.status()};const Ga=await ozt();return Lr.throwIfAborted(),{ok:!0,diagnostics:ti.diagnostics,run:Ga,mobilePreview:N.status()}}}}}});function ht(){St.reset(),X==null||X(),X=null,pe!==null&&(pe.stop(),r.close(pe.id),wt.flush(pe.id),pe=null,fe=null)}async function Ue(Xn,Lr){return(pe==null?void 0:pe.id)===Xn?pe.files.get(Lr):r.read(Xn,Lr).catch(ti=>{if(!yI(ti))throw ti})}function Wt(Xn,Lr,ti,Ga){const Za=lt.then(async()=>{if(Ga==null||Ga.signal.throwIfAborted(),Ga!==void 0&&((pe==null?void 0:pe.id)!==Xn||!pzt(pe.files.get(Lr),Ga.expected)))throw new Error("This file changed. Read it again before writing.");await r.write(Xn,Lr,ti),wt.touched(Xn,Lr),(pe==null?void 0:pe.id)===Xn&&(pe.files.set(Lr,ti),pe.changed(Lr))});return lt=Za.catch(el=>console.error("[editor] file write failed",el)),Za}function $e(Xn,Lr){const ti=lt.then(async()=>{const Ga=await r.remove(Xn,Lr);return Ga&&wt.touched(Xn,Lr),Ga&&(pe==null?void 0:pe.id)===Xn&&pe.files.delete(Lr)&&pe.changed(Lr),Ga});return lt=ti.catch(Ga=>console.error("[editor] file removal failed",Ga)),ti}function Me(Xn){const Lr=Tt();if(Lr.id!==Xn)throw new Error("that scene is not the one open");return{files:()=>Lr.files,write:(ti,Ga)=>Wt(Xn,ti,Ga),remove:async ti=>{await $e(Xn,ti)}}}async function $t(Xn){var Ga;const Lr=N0(await Ue(Xn,"scene.json").catch(()=>{})),ti=(((Ga=Lr==null?void 0:Lr.display)==null?void 0:Ga.title)??"").replace(/[\x00-\x1f\x7f]/g,"").replace(new RegExp(mqt.source,"g"),"").trim().slice(0,SEr);return ti===""?Xn:ti}async function vt(Xn,Lr){const ti=(await r.list()).find(Za=>Za.id===Xn);if(ti===void 0)return null;const Ga=await r.duplicate(Xn,`${ti.name} ${Lr}`,wt.remoteIds());return await tn(Ga.id,Za=>{var el;return{...Za,display:{...Za.display??{},title:`${((el=Za.display)==null?void 0:el.title)||ti.name} ${Lr}`}}}),Ga.id}const wt=jkr({store:r,session:()=>{const Xn=l();return s===null||Xn===null?null:{api:s,wallet:Xn}},account:()=>s===null?"unavailable":l()===null?"guest":"on",hash:Gxe,title:$t,duplicate:async Xn=>{const Lr=await vt(Xn,BPt(new Date));if(Lr===null)throw new Error("The scene is no longer on this device");return Lr}});let dt=!1,Nt=0;async function Ut(){const Xn=++Nt,Lr=l()!==null&&(await St.shell.aiProviders()).some(ti=>ti.available);Xn!==Nt||Lr===dt||(dt=Lr,Qb())}Ut();let qt=l();const Re=setInterval(()=>{const Xn=l();Xn!==qt&&(qt=Xn,St.reset(),Qb(),Ut(),wt.refresh(!0),Ee(fe).catch(Lr=>console.warn("[editor] could not clear the previews",Lr)))},wEr),Ke=()=>{document.visibilityState==="hidden"&&wt.flush()};document.addEventListener("visibilitychange",Ke);const st=new Set;async function tn(Xn,Lr){const ti=N0(await Ue(Xn,"scene.json"));if(ti===null)throw new Error("scene.json is missing or is not valid JSON");await Wt(Xn,"scene.json",VOt(Lr(ti)))}async function Cn(Xn,Lr){if(Lr==null||Lr==="")return null;const ti=await Ue(Xn,Lr).catch(()=>{});return ti===void 0||ti.length>vEr?null:WOt(Lr,ti)}async function un(Xn,Lr=wt.status().projects){var Ga,Za,el,uu;const ti=N0(await Ue(Xn.id,"scene.json").catch(()=>{}));return{sync:Lr[Xn.id],path:Xn.id,name:Xn.name,title:((Ga=ti==null?void 0:ti.display)==null?void 0:Ga.title)||Xn.name,world:((Za=ti==null?void 0:ti.worldConfiguration)==null?void 0:Za.name)||null,parcels:Array.isArray((el=ti==null?void 0:ti.scene)==null?void 0:el.parcels)?ti.scene.parcels.length:0,thumbnail:await Cn(Xn.id,(uu=ti==null?void 0:ti.display)==null?void 0:uu.navmapThumbnail),favourite:Xn.favourite,lastOpened:Xn.lastOpened,published:Xn.published,deletedAt:Xn.deletedAt??void 0}}async function Tn(Xn,Lr){var Ga,Za;ht(),f.length=0,L({type:"sessionStarted"});const ti=el=>{if(ht(),Lr===qe){A(`✖ ${el}`);for(const uu of T)uu(el)}};try{if(await wt.prepareOpen(Xn,(ii,yl)=>A(`Downloading from your account… ${ii}/${yl}`)),Lr!==qe)return;if(!await r.open(Xn)){ti("This scene is open in another tab. Close it there to edit it here.");return}const el=await r.readAll(Xn).then(async ii=>{for(const[yl,ja]of kSr(ii))await Wt(Xn,yl,ja),ii.set(yl,ja);return ii}).catch(ii=>{throw r.close(Xn),ii});if(Lr!==qe){r.close(Xn);return}const uu=await he(Xn);if(Lr!==qe){r.close(Xn);return}const lr=Z.open(Xn,el,uu);pe=lr;const Pl=Z.realmUrl(uu);if(fe=Pl,A(`▶ Opening ${((Za=(Ga=N0(lr.files.get("scene.json")))==null?void 0:Ga.display)==null?void 0:Za.title)||Xn}`),!await lr.start()&&!await lr.published()){if(Lr!==qe)return;A("✖ The scene did not build.");for(const ii of T)ii("The scene did not build.");return}if(await r.patch(Xn,{lastOpened:Date.now()}),Lr!==qe){ht();return}const fm=N0(lr.files.get("scene.json"))??{};te=O9e(fm,Pl),X=lEr(ii=>t.engineConsole(ii),A);for(const ii of x)ii(te)}catch(el){ti(kEr(el))}}function fi(Xn){N.close(),te=null;const Lr=++qe,ti=St.reset(),Ga=xt.then(async()=>{await ti,await lt,Lr===qe&&await Xn(Lr)});return xt=Ga.catch(Za=>console.error("[editor] project transition failed",Za)),Ga}function Tt(){if(pe===null)throw new Error("no project is open");return pe}const Wi={saveFile:async({path:Xn,content:Lr})=>Wt(Tt().id,Xn,Lr),getFile:async({path:Xn})=>{const Lr=Tt().files.get(Xn)??D2r(Xn);if(Lr===void 0)throw new Error(`no such file: ${Xn}`);return{content:Lr}},removeFiles:async({filePaths:Xn})=>{const Lr=Tt(),ti=[],Ga=[];for(const Za of Xn)(await $e(Lr.id,Za).catch(()=>!1)?ti:Ga).push(Za);return{success:ti,failed:Ga}},getFilesSizes:async({path:Xn,ignore:Lr})=>{const ti=Xn===""?"":`${Xn.replace(/\/+$/,"")}/`;return{files:[...Tt().files.keys()].filter(Za=>Za.startsWith(ti)&&!Za.split("/").some(el=>Lr.includes(el))).sort().map(Za=>({path:Za}))}}},Er={async find(Xn){return(await r.list()).some(Lr=>Lr.id===Xn)?!0:(await wt.refresh(!0),wt.remoteOnly().some(Lr=>Lr.id===Xn))},files:async Xn=>(pe==null?void 0:pe.id)===Xn?new Map(pe.files):r.readAll(Xn),manifest:Xn=>wt.manifest(Xn),build:async(Xn,Lr)=>Z.build(Xn,await Er.files(Xn),Lr),dependencies:async Xn=>dqt(await Ue(Xn,"package.json")),onImported:Xn=>(st.add(Xn),()=>st.delete(Xn))},to=a===null?null:Tkr({host:a,setWorld:async(Xn,Lr)=>{var Ga,Za;((Za=(Ga=N0(await Ue(Xn,"scene.json")))==null?void 0:Ga.worldConfiguration)==null?void 0:Za.name)!==Lr&&await tn(Xn,el=>({...el,worldConfiguration:{...el.worldConfiguration??{},name:Lr}}))},files:Xn=>Er.files(Xn),build:(Xn,Lr)=>Z.build(Xn,Lr,{production:!0}),published:(Xn,Lr)=>r.patch(Xn,{published:Lr})}),Ba=(a==null?void 0:a.services.projects)==null?null:_Er(Rkr(a.services.projects,(Xn,Lr)=>a.signedFetch(Xn,Lr)),Gxe);Opr(jTr(t.editorBase)),BTr(t.editorBase),mzt({material:UTr,sound:$Tr}),smr(async()=>yEr(await Er.files(Tt().id)));const Vo=z2r({builtin:Kkr(t.editorBase,TB("prefabs/catalog.json"),LTe),user:()=>Ba!==null&&l()!==null?Ba.tree:null}),Kc={...(a==null?void 0:a.services.projects)==null?{}:{...St.shell,aiOffered:()=>dt,aiTranscript:()=>{const Xn=qt;if(Xn===null||pe===null)return null;const Lr=pe.id;return{owner:`${Lr}@${Xn.toLowerCase()}`,load:()=>r.transcript(Lr,Xn),save:ti=>r.setTranscript(Lr,Xn,ti)}}},pickProject:async()=>{},openProject:Xn=>fi(Lr=>Tn(Xn,Lr)),relaunchProject:Xn=>fi(Lr=>Tn(Xn,Lr)),closeProject:()=>fi(async()=>{ht(),L({type:"sessionStarted"}),A("■ Scene closed"),await Ee(null).catch(Xn=>console.warn("[editor] could not clear the previews",Xn))}),getState:async()=>{const Xn=await r.list(),Lr=wt.status().projects,ti=wt.remoteOnly().map(Ga=>({path:Ga.id,name:Ga.name,title:Ga.name,world:null,parcels:0,thumbnail:null,remoteOnly:!0}));return{recentProjects:[...Xn].sort((Ga,Za)=>Za.lastOpened-Ga.lastOpened).map(Ga=>Ga.id),projects:[...await Promise.all(Xn.map(Ga=>un(Ga,Lr))),...ti],logs:[...f],bevyWebDir:"",editorSceneDir:"",webPort:0,scenePort:0,editorScenePort:0,favourites:Xn.filter(Ga=>Ga.favourite).map(Ga=>Ga.id)}},onStackLog:Xn=>{m.push(Xn)},onBuildEvent:Xn=>{_.push(Xn)},onServersReady:Xn=>{x.push(Xn)},requestReady:async()=>te,phonePreview:async Xn=>te===null?{ok:!1,reason:"no-scene"}:N.open(te.realm,te.position,Xn),phonePreviewState:N.state,onPhonePreviewState:N.subscribe,closePhonePreview:N.close,capturePhonePreview:N.capture,onServersError:Xn=>{T.push(Xn)},toggleFavourite:async Xn=>{const Lr=(await r.list()).find(ti=>ti.id===Xn);Lr!==void 0&&await r.patch(Xn,{favourite:!Lr.favourite})},deleteProject:async Xn=>{const Lr=await r.trash(Xn);return Lr&&wt.trashed(Xn),Lr},trashedProjects:async()=>Promise.all((await r.trashed()).map(Xn=>un(Xn))),restoreProject:async Xn=>{const Lr=await r.restore(Xn);return Lr&&wt.restored(Xn),Lr},syncStatus:async()=>(wt.refresh(),wt.status()),onSyncStatus:Xn=>wt.subscribe(Xn),resolveSyncConflict:(Xn,Lr)=>wt.resolve(Xn,Lr),exportProject:async Xn=>Ykr(await $t(Xn),await Er.files(Xn)),importProject:async Xn=>{const Lr=await Zkr(Xn,async(ti,Ga)=>{const Za=await r.create(ti,Ga,{taken:wt.remoteIds()});return wt.touched(Za.id,"scene.json"),Za.id});if((Lr==null?void 0:Lr.ok)===!0){const{id:ti,needsPackages:Ga}=Lr.report;Object.keys(Ga).length>0&&r.readAll(ti).then(Za=>Z.install(ti,Za)).catch(Za=>console.warn("[editor] could not install the packages of",ti,Za));for(const Za of st)Za(Lr.report)}return Lr},projectPackages:async Xn=>B.get(Xn)??null,onProjectPackages:Xn=>(G.add(Xn),()=>G.delete(Xn)),...to===null||a===null?{}:{publishScene:(Xn,Lr,ti)=>to.publish(Xn,Lr,ti),...to.publishShared===void 0?{}:{publishShared:to.publishShared},publishStop:async()=>to.stop(),publishWorlds:async()=>wkr(l(),a.services.worldsContent)},canImportFolder:Qkr(),renameProject:(Xn,Lr)=>tn(Xn,ti=>({...ti,display:{...ti.display??{},title:Lr.trim()}})),duplicateProject:Xn=>vt(Xn,"copy"),sceneTemplates:async()=>P2r(),createScene:async(Xn,Lr,ti)=>{const Ga=Eqt(ti),Za=N0(Ga.get("scene.json"));if(Za===null)throw new Error(`template not found: ${ti}`);Ga.set("scene.json",VOt({...Za,display:{...Za.display??{},title:Lr.trim()}}));const el=await r.create(Lr,Ga,{taken:wt.remoteIds()});return wt.touched(el.id,"scene.json"),el.id},openExternal:async Xn=>{/^https?:$/.test(new URL(Xn).protocol)&&window.open(Xn,"_blank","noopener,noreferrer")},compositeEntityIds:async Xn=>{for(const Lr of GTr){const ti=await Ue(Xn,Lr);if(ti!==void 0)return HTr(new TextDecoder().decode(ti))}return[]},sceneSettings:async Xn=>{const Lr=N0(await Ue(Xn,"scene.json"));if(Lr===null)throw new Error("scene.json is missing or is not valid JSON");const ti=zTr(Lr);return{...ti,thumbnail:await Cn(Xn,ti.thumbnailPath)}},saveSceneSettings:async(Xn,Lr)=>{const ti=VTr(Lr);return ti!==null?ti:(await tn(Xn,Ga=>WTr(Ga,Lr)),null)},pickSceneThumbnail:async Xn=>{const Lr=await kqt(Object.values(iqe).join(","));if(Lr===null)return null;const ti=lqt(Lr.name);if(iqe[ti]===void 0)throw new Error("not a supported image (png, jpg, gif, webp)");const Ga=`${xEr}/scene-thumbnail${ti}`,Za=new Uint8Array(await Lr.arrayBuffer());return await Wt(Xn,Ga,Za),{path:Ga,dataUrl:WOt(Ga,Za)}},appVersion:async()=>ITr,prefabLibraryList:()=>Vo.list(),prefabLibraryCopyIn:async(Xn,Lr)=>Vo.copyIn(Xn,Me(Lr)),prefabLibraryUpdateCopy:async(Xn,Lr)=>Vo.updateCopy(Xn,Me(Lr)),prefabUserLibrary:()=>Ba===null?"unavailable":l()===null?"guest":"on",...Ba===null?{}:{prefabLibraryCopyOut:async(Xn,Lr)=>{if(l()===null)throw new Error(TEr);return Ba.copyOut(Me(Xn),Lr)},prefabLibraryDelete:async Xn=>{const Lr=Aqt(Xn);return(Lr==null?void 0:Lr.scope)==="user"&&l()!==null?Ba.remove(Lr.name):!1}}};async function Hl(){const Xn=new Set;for(const Lr of[...await r.list(),...await r.trashed()]){const ti=KWt(await Ue(Lr.id,FR).catch(()=>{}));for(const Ga of Object.keys((ti==null?void 0:ti.packages)??{}))Xn.add(Ga);for(const[Ga,Za]of Object.entries((ti==null?void 0:ti.dependencies)??{}))Xn.add(Zj(Ga,Za))}for(const Lr of await r.packages.keys())Xn.has(Lr)||await r.packages.remove(Lr)}return r.purge(Date.now()-bEr).then(async Xn=>{const Lr=await caches.open(Qxe),ti=Xn.map(({id:Za,previewId:el})=>Z.realmUrl(el??Za));for(const Za of await Lr.keys())ti.some(el=>Za.url.startsWith(el))&&await Lr.delete(Za);const Ga=await caches.open(OTr);for(const Za of ti)await Ga.delete(`${Za}__storage`);Xn.length>0&&await Hl()}).catch(Xn=>console.warn("[editor] could not empty the trash",Xn)),{shell:Kc,projects:Er,dataLayer:Wi,dispose:()=>{N.dispose(),St.dispose(),qe++,ht(),Z.dispose(),clearInterval(Re),document.removeEventListener("visibilitychange",Ke),wt.flush().finally(()=>wt.dispose())}}};let Kj=null,EG=null,sre=null,kPt=!1;function AEr(){if(Kj!==null)return Kj;const t=window.__dclEditorHost;if(t===void 0||t.version!==0&&t.version!==1)return console.error("[editor] window.__dclEditorHost (contract v0 or v1) is missing: nothing to mount into"),null;console.log("[editor] web build 2026-10-31T02:58:14.263Z"),VPt(t),CNt(a=>t.engineConsole(a)),U3t(()=>t.identity()),YHn.needed=!1;const n=EEr({host:t,sceneCompiler:FTr(t.editorBase)});window.editorShell=CEr(n.shell,t),rRt(n.dataLayer??null),dHe();const r=document.createElement("link");return r.rel="stylesheet",r.href=new URL("editor.css",t.editorBase).href,r.integrity=TB("editor.css"),r.crossOrigin="anonymous",r.onerror=()=>console.error("[editor] editor.css did not load or does not match the hash this editor was published with: its fonts are not applied"),document.head.appendChild(r),G1.project=null,G1.notice=null,Kj={host:t,web:n,fonts:r},Kj}function Kxe(){const t=EG;EG=null,t==null||t.unmount()}function CEr(t,n){const{createScene:r,importProject:a,duplicateProject:s}=t;return{...t,...r===void 0?{}:{createScene:async(l,f,m)=>{const _=await r(l,f,m);return _!==null&&tw("project_created",{project_id:_,source:"template",template:m}),_}},...a===void 0?{}:{importProject:async l=>{const f=await a(l);return(f==null?void 0:f.ok)===!0&&tw("project_created",{project_id:f.report.id,source:"import",import_kind:l}),f}},...s===void 0?{}:{duplicateProject:async l=>{const f=await s(l);return f!==null&&tw("project_created",{project_id:f,source:"duplicate",from_project_id:l}),f}},openProject:async l=>{const f=EG;Kxe(),f==null||f.api.close(),sre??(sre=jjt(n.container,C.jsx(cSr,{}))),n.setMode("edit"),G1.project=l,G1.opens++,await t.openProject(l),tw("project_opened")}}}function OEr(t,n){var m;const r=AEr();if(r===null)return()=>{};Kxe();const a=document.createElement("div");a.style.cssText="position:relative;width:100%;height:100%",PHn(a),t.appendChild(a);const s=jjt(a,C.jsx(DHn,{}),"eui-create-root"),l={api:n,unmount:()=>{s(),a.remove()}};EG=l;const f=kPt?null:((m=Ok(r.host))==null?void 0:m.openProject)??null;return kPt=!0,tw("editor_opened",{deep_link:f!==null}),f!==null&&r.web.projects.find(f).catch(()=>!1).then(_=>{var x;EG===l&&(_?(x=window.editorShell)==null||x.openProject(f):G1.notice=`“${f}” is not on this device or on your account. Here are your scenes.`)}),()=>{EG===l&&Kxe()}}function PEr(){Kxe();const t=Kj;if(t===null)return;Kj=null;const n=WE();sre==null||sre(),sre=null,t.fonts.remove(),G1.project=null,delete window.editorShell,U3t(null),CNt(null),VPt(null),t.host.setMode("off"),n.finally(()=>{var r,a;(a=(r=t.web).dispose)==null||a.call(r),Kj===null&&rRt(null)})}window.__dclEditor={mountHome:OEr,unmount:PEr};
37693
+ `;L.set("data.json",new TextEncoder().encode(Z));const te=pqe(Cqt(G,T)).slice(0,pEr).replace(/_+$/,"")||"prefab",pe=new Set((await t.list()).map(X=>X.name));let fe=1;for(let X=0;X<mEr;X++){let he=te;for(;pe.has(he);)he=`${te}_${++fe}`;try{return await f(he,L),{ref:`user:${he}`,scope:"user",data:Z}}catch(Ee){if(!(Ee instanceof _qt))throw Ee;pe.add(he)}}throw new Error("could not find a free name in your library — try again")},remove:m=>t.remove(m)}}const SPt=new WeakMap;function gEr(t){let n=SPt.get(t);return n===void 0&&(n=crypto.subtle.digest("SHA-256",new Uint8Array(t)).then(r=>[...new Uint8Array(r)].map(a=>a.toString(16).padStart(2,"0")).join("")),SPt.set(t,n)),n}async function yEr(t){const n=t.get(".dclignore"),r=yZe(t.keys(),n===void 0?void 0:new TextDecoder().decode(n)).filter(l=>l!==FR),a=new Map;let s=0;for(const l of r){const f=t.get(l);f!==void 0&&(a.set(await gEr(f),f.byteLength),s++)}return{bytes:[...a.values()].reduce((l,f)=>l+f,0),files:s}}const TPt=4e3,vEr=4e6,bEr=UPt*24*60*60*1e3,xEr="assets/images",wEr=3e3,SEr=100,TEr="Sign in to keep prefabs in your own library. The built-in prefabs work without it.",kEr=t=>t instanceof Error?t.message:String(t);function O9e(t,n){var r,a,s;return{realm:n.replace(/\/$/,""),systemScene:"",position:((r=t.scene)==null?void 0:r.base)??((s=(a=t.scene)==null?void 0:a.parcels)==null?void 0:s[0])??"0,0",spawn:qTr(t),spawnPoints:JSON.stringify(Array.isArray(t.spawnPoints)?t.spawnPoints:[])}}const EEr=({host:t,sceneCompiler:n})=>{const r=new Ikr(kkr()),a=Ok(t),s=a===null||a.services.projects===null?null:hqt(a.services.projects,(Xn,Lr)=>a.signedFetch(Xn,Lr)),l=()=>{const{address:Xn,isGuest:Lr}=t.identity();return Lr||Xn===null?null:Xn.toLowerCase()},f=[],m=[],_=[],x=[],T=[],A=Xn=>{f.push(Xn),f.length>TPt&&f.splice(0,f.length-TPt);for(const Lr of m)Lr(Xn)},N=rEr(tEr(t.pageDir,(a==null?void 0:a.services.projects)??null,location.search),he,async Xn=>bPt(await Y,l()??"guest",Xn),nEr((a==null?void 0:a.services.worldsContent)??null)),L=Xn=>{for(const Lr of _)Lr(Xn);Xn.type==="bundleSaved"&&pe!==null&&N.built(pe.id).catch(Lr=>console.error("[editor] mobile preview reload failed",Lr))},B=new Map,G=new Set,Y=I3t.then(H2r),Z=X2r({pageDir:t.pageDir,urls:n,machineId:async Xn=>bPt(await Y,l()??"guest",Xn),emit:L,log:A,packages:r.packages,saveLock:async(Xn,Lr)=>{await r.write(Xn,FR,Lr),wt.touched(Xn,FR),(pe==null?void 0:pe.id)===Xn&&pe.files.set(FR,Lr)},onPackages:(Xn,Lr)=>{if(Lr===null){B.delete(Xn);return}B.set(Xn,Lr);for(const ti of G)ti(Xn,Lr)}});let te=null,pe=null,fe=null,X=null;async function he(Xn){var Ga;const Lr=(Ga=(await r.list()).find(Za=>Za.id===Xn))==null?void 0:Ga.previewId;if(Lr!==void 0)return Lr;const ti="p"+Array.from(crypto.getRandomValues(new Uint8Array(16)),Za=>Za.toString(16).padStart(2,"0")).join("");return await r.patch(Xn,{previewId:ti}),ti}async function Ee(Xn){const Lr=new URL("preview/",t.pageDir).href,ti=await caches.open(Qxe);for(const Ga of await ti.keys())Ga.url.startsWith(Lr)&&(Xn===null||!Ga.url.startsWith(Xn))&&await ti.delete(Ga)}let qe=0,xt=Promise.resolve(),lt=Promise.resolve();const St=RTr({service:(a==null?void 0:a.services.projects)??null,fetch:(Xn,Lr)=>a===null?Promise.reject(new Error("The assistant service is not configured.")):a.signedFetch(Xn,Lr),wallet:l,mobilePreview:N.status,memory:{read:async(Xn,Lr)=>{const ti=await r.assistantMemory(Xn,Lr);return vc(ti)&&typeof ti.id=="string"&&typeof ti.key=="string"?{id:ti.id,key:ti.key}:null},write:(Xn,Lr,ti)=>r.setAssistantMemory(Xn,Lr,ti)},project:()=>{const Xn=Tt();return{id:Xn.id,tools:{files:()=>Xn.files,write:(Lr,ti,Ga,Za)=>Wt(Xn.id,Lr,ti,{expected:Ga,signal:Za}),assets:czt({files:()=>Xn.files}),scene:(Lr,ti)=>ATe(Lr,ti,async Ga=>{var ii,yl,ja;const Za=ss=>{var Ts,Zs;return JSON.stringify([(Ts=ss==null?void 0:ss.scene)==null?void 0:Ts.parcels,(Zs=ss==null?void 0:ss.scene)==null?void 0:Zs.base])};await WE();const el=Za(N0(await Ue(Xn.id,"scene.json")));await tn(Xn.id,ss=>QTr(ss,Ga));const uu=N0(await Ue(Xn.id,"scene.json")),lr=Za(uu)!==el,Pl=((ii=uu==null?void 0:uu.scene)==null?void 0:ii.parcels)??[],fm=(yl=nt.scene)==null?void 0:yl.parcels.map(ss=>`${ss.x},${ss.y}`).sort().join(";");if(lr||fm!==[...Pl].sort().join(";")){if(!await Xn.flush())throw new Error("The scene did not build. Fix the code and retry sceneSettings to load the new parcels.");ti.throwIfAborted();const ss=await he(Xn.id);te=O9e(uu??{},Z.realmUrl(ss)),await uEr(Pl,()=>{for(const Ts of x)Ts(te)},ti)}else if(te!==null){const ss=O9e(uu??{},te.realm);(ss.spawn!==te.spawn||ss.spawnPoints!==te.spawnPoints)&&(te={...te,spawn:ss.spawn,spawnPoints:ss.spawnPoints},qdr(ss.spawn,ss.spawnPoints))}return{requiredPermissions:(uu==null?void 0:uu.requiredPermissions)??[],spawnPoints:(uu==null?void 0:uu.spawnPoints)??[],parcels:Pl,base:((ja=uu==null?void 0:uu.scene)==null?void 0:ja.base)??null,previewReady:!0}}),written:dzt,view:(Lr,ti)=>CTe(Lr,ti),playtest:YXe,uiLayout:PWe,turnEnded:oVt,build:async Lr=>{Lr.throwIfAborted(),await lt;const ti=await Z.build(Xn.id,new Map(Xn.files));if(Lr.throwIfAborted(),!ti.ok)return{ok:!1,diagnostics:ti.diagnostics,mobilePreview:N.status()};const Ga=await ozt();return Lr.throwIfAborted(),{ok:!0,diagnostics:ti.diagnostics,run:Ga,mobilePreview:N.status()}}}}}});function ht(){St.reset(),X==null||X(),X=null,pe!==null&&(pe.stop(),r.close(pe.id),wt.flush(pe.id),pe=null,fe=null)}async function Ue(Xn,Lr){return(pe==null?void 0:pe.id)===Xn?pe.files.get(Lr):r.read(Xn,Lr).catch(ti=>{if(!yI(ti))throw ti})}function Wt(Xn,Lr,ti,Ga){const Za=lt.then(async()=>{if(Ga==null||Ga.signal.throwIfAborted(),Ga!==void 0&&((pe==null?void 0:pe.id)!==Xn||!pzt(pe.files.get(Lr),Ga.expected)))throw new Error("This file changed. Read it again before writing.");await r.write(Xn,Lr,ti),wt.touched(Xn,Lr),(pe==null?void 0:pe.id)===Xn&&(pe.files.set(Lr,ti),pe.changed(Lr))});return lt=Za.catch(el=>console.error("[editor] file write failed",el)),Za}function $e(Xn,Lr){const ti=lt.then(async()=>{const Ga=await r.remove(Xn,Lr);return Ga&&wt.touched(Xn,Lr),Ga&&(pe==null?void 0:pe.id)===Xn&&pe.files.delete(Lr)&&pe.changed(Lr),Ga});return lt=ti.catch(Ga=>console.error("[editor] file removal failed",Ga)),ti}function Me(Xn){const Lr=Tt();if(Lr.id!==Xn)throw new Error("that scene is not the one open");return{files:()=>Lr.files,write:(ti,Ga)=>Wt(Xn,ti,Ga),remove:async ti=>{await $e(Xn,ti)}}}async function $t(Xn){var Ga;const Lr=N0(await Ue(Xn,"scene.json").catch(()=>{})),ti=(((Ga=Lr==null?void 0:Lr.display)==null?void 0:Ga.title)??"").replace(/[\x00-\x1f\x7f]/g,"").replace(new RegExp(mqt.source,"g"),"").trim().slice(0,SEr);return ti===""?Xn:ti}async function vt(Xn,Lr){const ti=(await r.list()).find(Za=>Za.id===Xn);if(ti===void 0)return null;const Ga=await r.duplicate(Xn,`${ti.name} ${Lr}`,wt.remoteIds());return await tn(Ga.id,Za=>{var el;return{...Za,display:{...Za.display??{},title:`${((el=Za.display)==null?void 0:el.title)||ti.name} ${Lr}`}}}),Ga.id}const wt=jkr({store:r,session:()=>{const Xn=l();return s===null||Xn===null?null:{api:s,wallet:Xn}},account:()=>s===null?"unavailable":l()===null?"guest":"on",hash:Gxe,title:$t,duplicate:async Xn=>{const Lr=await vt(Xn,BPt(new Date));if(Lr===null)throw new Error("The scene is no longer on this device");return Lr}});let dt=!1,Nt=0;async function Ut(){const Xn=++Nt,Lr=l()!==null&&(await St.shell.aiProviders()).some(ti=>ti.available);Xn!==Nt||Lr===dt||(dt=Lr,Qb())}Ut();let qt=l();const Re=setInterval(()=>{const Xn=l();Xn!==qt&&(qt=Xn,St.reset(),Qb(),Ut(),wt.refresh(!0),Ee(fe).catch(Lr=>console.warn("[editor] could not clear the previews",Lr)))},wEr),Ke=()=>{document.visibilityState==="hidden"&&wt.flush()};document.addEventListener("visibilitychange",Ke);const st=new Set;async function tn(Xn,Lr){const ti=N0(await Ue(Xn,"scene.json"));if(ti===null)throw new Error("scene.json is missing or is not valid JSON");await Wt(Xn,"scene.json",VOt(Lr(ti)))}async function Cn(Xn,Lr){if(Lr==null||Lr==="")return null;const ti=await Ue(Xn,Lr).catch(()=>{});return ti===void 0||ti.length>vEr?null:WOt(Lr,ti)}async function un(Xn,Lr=wt.status().projects){var Ga,Za,el,uu;const ti=N0(await Ue(Xn.id,"scene.json").catch(()=>{}));return{sync:Lr[Xn.id],path:Xn.id,name:Xn.name,title:((Ga=ti==null?void 0:ti.display)==null?void 0:Ga.title)||Xn.name,world:((Za=ti==null?void 0:ti.worldConfiguration)==null?void 0:Za.name)||null,parcels:Array.isArray((el=ti==null?void 0:ti.scene)==null?void 0:el.parcels)?ti.scene.parcels.length:0,thumbnail:await Cn(Xn.id,(uu=ti==null?void 0:ti.display)==null?void 0:uu.navmapThumbnail),favourite:Xn.favourite,lastOpened:Xn.lastOpened,published:Xn.published,deletedAt:Xn.deletedAt??void 0}}async function Tn(Xn,Lr){var Ga,Za;ht(),f.length=0,L({type:"sessionStarted"});const ti=el=>{if(ht(),Lr===qe){A(`✖ ${el}`);for(const uu of T)uu(el)}};try{if(await wt.prepareOpen(Xn,(ii,yl)=>A(`Downloading from your account… ${ii}/${yl}`)),Lr!==qe)return;if(!await r.open(Xn)){ti("This scene is open in another tab. Close it there to edit it here.");return}const el=await r.readAll(Xn).then(async ii=>{for(const[yl,ja]of kSr(ii))await Wt(Xn,yl,ja),ii.set(yl,ja);return ii}).catch(ii=>{throw r.close(Xn),ii});if(Lr!==qe){r.close(Xn);return}const uu=await he(Xn);if(Lr!==qe){r.close(Xn);return}const lr=Z.open(Xn,el,uu);pe=lr;const Pl=Z.realmUrl(uu);if(fe=Pl,A(`▶ Opening ${((Za=(Ga=N0(lr.files.get("scene.json")))==null?void 0:Ga.display)==null?void 0:Za.title)||Xn}`),!await lr.start()&&!await lr.published()){if(Lr!==qe)return;A("✖ The scene did not build.");for(const ii of T)ii("The scene did not build.");return}if(await r.patch(Xn,{lastOpened:Date.now()}),Lr!==qe){ht();return}const fm=N0(lr.files.get("scene.json"))??{};te=O9e(fm,Pl),X=lEr(ii=>t.engineConsole(ii),A);for(const ii of x)ii(te)}catch(el){ti(kEr(el))}}function fi(Xn){N.close(),te=null;const Lr=++qe,ti=St.reset(),Ga=xt.then(async()=>{await ti,await lt,Lr===qe&&await Xn(Lr)});return xt=Ga.catch(Za=>console.error("[editor] project transition failed",Za)),Ga}function Tt(){if(pe===null)throw new Error("no project is open");return pe}const Wi={saveFile:async({path:Xn,content:Lr})=>Wt(Tt().id,Xn,Lr),getFile:async({path:Xn})=>{const Lr=Tt().files.get(Xn)??D2r(Xn);if(Lr===void 0)throw new Error(`no such file: ${Xn}`);return{content:Lr}},removeFiles:async({filePaths:Xn})=>{const Lr=Tt(),ti=[],Ga=[];for(const Za of Xn)(await $e(Lr.id,Za).catch(()=>!1)?ti:Ga).push(Za);return{success:ti,failed:Ga}},getFilesSizes:async({path:Xn,ignore:Lr})=>{const ti=Xn===""?"":`${Xn.replace(/\/+$/,"")}/`;return{files:[...Tt().files.keys()].filter(Za=>Za.startsWith(ti)&&!Za.split("/").some(el=>Lr.includes(el))).sort().map(Za=>({path:Za}))}}},Er={async find(Xn){return(await r.list()).some(Lr=>Lr.id===Xn)?!0:(await wt.refresh(!0),wt.remoteOnly().some(Lr=>Lr.id===Xn))},files:async Xn=>(pe==null?void 0:pe.id)===Xn?new Map(pe.files):r.readAll(Xn),manifest:Xn=>wt.manifest(Xn),build:async(Xn,Lr)=>Z.build(Xn,await Er.files(Xn),Lr),dependencies:async Xn=>dqt(await Ue(Xn,"package.json")),onImported:Xn=>(st.add(Xn),()=>st.delete(Xn))},to=a===null?null:Tkr({host:a,setWorld:async(Xn,Lr)=>{var Ga,Za;((Za=(Ga=N0(await Ue(Xn,"scene.json")))==null?void 0:Ga.worldConfiguration)==null?void 0:Za.name)!==Lr&&await tn(Xn,el=>({...el,worldConfiguration:{...el.worldConfiguration??{},name:Lr}}))},files:Xn=>Er.files(Xn),build:(Xn,Lr)=>Z.build(Xn,Lr,{production:!0}),published:(Xn,Lr)=>r.patch(Xn,{published:Lr})}),Ba=(a==null?void 0:a.services.projects)==null?null:_Er(Rkr(a.services.projects,(Xn,Lr)=>a.signedFetch(Xn,Lr)),Gxe);Opr(jTr(t.editorBase)),BTr(t.editorBase),mzt({material:UTr,sound:$Tr}),smr(async()=>yEr(await Er.files(Tt().id)));const Vo=z2r({builtin:Kkr(t.editorBase,TB("prefabs/catalog.json"),LTe),user:()=>Ba!==null&&l()!==null?Ba.tree:null}),Kc={...(a==null?void 0:a.services.projects)==null?{}:{...St.shell,aiOffered:()=>dt,aiTranscript:()=>{const Xn=qt;if(Xn===null||pe===null)return null;const Lr=pe.id;return{owner:`${Lr}@${Xn.toLowerCase()}`,load:()=>r.transcript(Lr,Xn),save:ti=>r.setTranscript(Lr,Xn,ti)}}},pickProject:async()=>{},openProject:Xn=>fi(Lr=>Tn(Xn,Lr)),relaunchProject:Xn=>fi(Lr=>Tn(Xn,Lr)),closeProject:()=>fi(async()=>{ht(),L({type:"sessionStarted"}),A("■ Scene closed"),await Ee(null).catch(Xn=>console.warn("[editor] could not clear the previews",Xn))}),getState:async()=>{const Xn=await r.list(),Lr=wt.status().projects,ti=wt.remoteOnly().map(Ga=>({path:Ga.id,name:Ga.name,title:Ga.name,world:null,parcels:0,thumbnail:null,remoteOnly:!0}));return{recentProjects:[...Xn].sort((Ga,Za)=>Za.lastOpened-Ga.lastOpened).map(Ga=>Ga.id),projects:[...await Promise.all(Xn.map(Ga=>un(Ga,Lr))),...ti],logs:[...f],bevyWebDir:"",editorSceneDir:"",webPort:0,scenePort:0,editorScenePort:0,favourites:Xn.filter(Ga=>Ga.favourite).map(Ga=>Ga.id)}},onStackLog:Xn=>{m.push(Xn)},onBuildEvent:Xn=>{_.push(Xn)},onServersReady:Xn=>{x.push(Xn)},requestReady:async()=>te,phonePreview:async Xn=>te===null?{ok:!1,reason:"no-scene"}:N.open(te.realm,te.position,Xn),phonePreviewState:N.state,onPhonePreviewState:N.subscribe,closePhonePreview:N.close,capturePhonePreview:N.capture,onServersError:Xn=>{T.push(Xn)},toggleFavourite:async Xn=>{const Lr=(await r.list()).find(ti=>ti.id===Xn);Lr!==void 0&&await r.patch(Xn,{favourite:!Lr.favourite})},deleteProject:async Xn=>{const Lr=await r.trash(Xn);return Lr&&wt.trashed(Xn),Lr},trashedProjects:async()=>Promise.all((await r.trashed()).map(Xn=>un(Xn))),restoreProject:async Xn=>{const Lr=await r.restore(Xn);return Lr&&wt.restored(Xn),Lr},syncStatus:async()=>(wt.refresh(),wt.status()),onSyncStatus:Xn=>wt.subscribe(Xn),resolveSyncConflict:(Xn,Lr)=>wt.resolve(Xn,Lr),exportProject:async Xn=>Ykr(await $t(Xn),await Er.files(Xn)),importProject:async Xn=>{const Lr=await Zkr(Xn,async(ti,Ga)=>{const Za=await r.create(ti,Ga,{taken:wt.remoteIds()});return wt.touched(Za.id,"scene.json"),Za.id});if((Lr==null?void 0:Lr.ok)===!0){const{id:ti,needsPackages:Ga}=Lr.report;Object.keys(Ga).length>0&&r.readAll(ti).then(Za=>Z.install(ti,Za)).catch(Za=>console.warn("[editor] could not install the packages of",ti,Za));for(const Za of st)Za(Lr.report)}return Lr},projectPackages:async Xn=>B.get(Xn)??null,onProjectPackages:Xn=>(G.add(Xn),()=>G.delete(Xn)),...to===null||a===null?{}:{publishScene:(Xn,Lr,ti)=>to.publish(Xn,Lr,ti),...to.publishShared===void 0?{}:{publishShared:to.publishShared},publishStop:async()=>to.stop(),publishWorlds:async()=>wkr(l(),a.services.worldsContent)},canImportFolder:Qkr(),renameProject:(Xn,Lr)=>tn(Xn,ti=>({...ti,display:{...ti.display??{},title:Lr.trim()}})),duplicateProject:Xn=>vt(Xn,"copy"),sceneTemplates:async()=>P2r(),createScene:async(Xn,Lr,ti)=>{const Ga=Eqt(ti),Za=N0(Ga.get("scene.json"));if(Za===null)throw new Error(`template not found: ${ti}`);Ga.set("scene.json",VOt({...Za,display:{...Za.display??{},title:Lr.trim()}}));const el=await r.create(Lr,Ga,{taken:wt.remoteIds()});return wt.touched(el.id,"scene.json"),el.id},openExternal:async Xn=>{/^https?:$/.test(new URL(Xn).protocol)&&window.open(Xn,"_blank","noopener,noreferrer")},compositeEntityIds:async Xn=>{for(const Lr of GTr){const ti=await Ue(Xn,Lr);if(ti!==void 0)return HTr(new TextDecoder().decode(ti))}return[]},sceneSettings:async Xn=>{const Lr=N0(await Ue(Xn,"scene.json"));if(Lr===null)throw new Error("scene.json is missing or is not valid JSON");const ti=zTr(Lr);return{...ti,thumbnail:await Cn(Xn,ti.thumbnailPath)}},saveSceneSettings:async(Xn,Lr)=>{const ti=VTr(Lr);return ti!==null?ti:(await tn(Xn,Ga=>WTr(Ga,Lr)),null)},pickSceneThumbnail:async Xn=>{const Lr=await kqt(Object.values(iqe).join(","));if(Lr===null)return null;const ti=lqt(Lr.name);if(iqe[ti]===void 0)throw new Error("not a supported image (png, jpg, gif, webp)");const Ga=`${xEr}/scene-thumbnail${ti}`,Za=new Uint8Array(await Lr.arrayBuffer());return await Wt(Xn,Ga,Za),{path:Ga,dataUrl:WOt(Ga,Za)}},appVersion:async()=>ITr,prefabLibraryList:()=>Vo.list(),prefabLibraryCopyIn:async(Xn,Lr)=>Vo.copyIn(Xn,Me(Lr)),prefabLibraryUpdateCopy:async(Xn,Lr)=>Vo.updateCopy(Xn,Me(Lr)),prefabUserLibrary:()=>Ba===null?"unavailable":l()===null?"guest":"on",...Ba===null?{}:{prefabLibraryCopyOut:async(Xn,Lr)=>{if(l()===null)throw new Error(TEr);return Ba.copyOut(Me(Xn),Lr)},prefabLibraryDelete:async Xn=>{const Lr=Aqt(Xn);return(Lr==null?void 0:Lr.scope)==="user"&&l()!==null?Ba.remove(Lr.name):!1}}};async function Hl(){const Xn=new Set;for(const Lr of[...await r.list(),...await r.trashed()]){const ti=KWt(await Ue(Lr.id,FR).catch(()=>{}));for(const Ga of Object.keys((ti==null?void 0:ti.packages)??{}))Xn.add(Ga);for(const[Ga,Za]of Object.entries((ti==null?void 0:ti.dependencies)??{}))Xn.add(Zj(Ga,Za))}for(const Lr of await r.packages.keys())Xn.has(Lr)||await r.packages.remove(Lr)}return r.purge(Date.now()-bEr).then(async Xn=>{const Lr=await caches.open(Qxe),ti=Xn.map(({id:Za,previewId:el})=>Z.realmUrl(el??Za));for(const Za of await Lr.keys())ti.some(el=>Za.url.startsWith(el))&&await Lr.delete(Za);const Ga=await caches.open(OTr);for(const Za of ti)await Ga.delete(`${Za}__storage`);Xn.length>0&&await Hl()}).catch(Xn=>console.warn("[editor] could not empty the trash",Xn)),{shell:Kc,projects:Er,dataLayer:Wi,dispose:()=>{N.dispose(),St.dispose(),qe++,ht(),Z.dispose(),clearInterval(Re),document.removeEventListener("visibilitychange",Ke),wt.flush().finally(()=>wt.dispose())}}};let Kj=null,EG=null,sre=null,kPt=!1;function AEr(){if(Kj!==null)return Kj;const t=window.__dclEditorHost;if(t===void 0||t.version!==0&&t.version!==1)return console.error("[editor] window.__dclEditorHost (contract v0 or v1) is missing: nothing to mount into"),null;console.log("[editor] web build 2026-10-31T03:03:18.259Z"),VPt(t),CNt(a=>t.engineConsole(a)),U3t(()=>t.identity()),YHn.needed=!1;const n=EEr({host:t,sceneCompiler:FTr(t.editorBase)});window.editorShell=CEr(n.shell,t),rRt(n.dataLayer??null),dHe();const r=document.createElement("link");return r.rel="stylesheet",r.href=new URL("editor.css",t.editorBase).href,r.integrity=TB("editor.css"),r.crossOrigin="anonymous",r.onerror=()=>console.error("[editor] editor.css did not load or does not match the hash this editor was published with: its fonts are not applied"),document.head.appendChild(r),G1.project=null,G1.notice=null,Kj={host:t,web:n,fonts:r},Kj}function Kxe(){const t=EG;EG=null,t==null||t.unmount()}function CEr(t,n){const{createScene:r,importProject:a,duplicateProject:s}=t;return{...t,...r===void 0?{}:{createScene:async(l,f,m)=>{const _=await r(l,f,m);return _!==null&&tw("project_created",{project_id:_,source:"template",template:m}),_}},...a===void 0?{}:{importProject:async l=>{const f=await a(l);return(f==null?void 0:f.ok)===!0&&tw("project_created",{project_id:f.report.id,source:"import",import_kind:l}),f}},...s===void 0?{}:{duplicateProject:async l=>{const f=await s(l);return f!==null&&tw("project_created",{project_id:f,source:"duplicate",from_project_id:l}),f}},openProject:async l=>{const f=EG;Kxe(),f==null||f.api.close(),sre??(sre=jjt(n.container,C.jsx(cSr,{}))),n.setMode("edit"),G1.project=l,G1.opens++,await t.openProject(l),tw("project_opened")}}}function OEr(t,n){var m;const r=AEr();if(r===null)return()=>{};Kxe();const a=document.createElement("div");a.style.cssText="position:relative;width:100%;height:100%",PHn(a),t.appendChild(a);const s=jjt(a,C.jsx(DHn,{}),"eui-create-root"),l={api:n,unmount:()=>{s(),a.remove()}};EG=l;const f=kPt?null:((m=Ok(r.host))==null?void 0:m.openProject)??null;return kPt=!0,tw("editor_opened",{deep_link:f!==null}),f!==null&&r.web.projects.find(f).catch(()=>!1).then(_=>{var x;EG===l&&(_?(x=window.editorShell)==null||x.openProject(f):G1.notice=`“${f}” is not on this device or on your account. Here are your scenes.`)}),()=>{EG===l&&Kxe()}}function PEr(){Kxe();const t=Kj;if(t===null)return;Kj=null;const n=WE();sre==null||sre(),sre=null,t.fonts.remove(),G1.project=null,delete window.editorShell,U3t(null),CNt(null),VPt(null),t.host.setMode("off"),n.finally(()=>{var r,a;(a=(r=t.web).dispose)==null||a.call(r),Kj===null&&rRt(null)})}window.__dclEditor={mountHome:OEr,unmount:PEr};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dcl-regenesislabs/web-editor",
3
- "version": "0.3.22-41162201857.commit-2a300c0",
3
+ "version": "0.3.22-41162568497.commit-968353b",
4
4
  "license": "Apache-2.0",
5
5
  "description": "The Decentraland scene editor for the web explorer: editor.js plus the files it loads beside it. Served by version, not installed.",
6
6
  "repository": {
@@ -8,5 +8,5 @@
8
8
  "url": "git+https://github.com/dcl-regenesislabs/web-editor.git",
9
9
  "directory": "packages/web"
10
10
  },
11
- "commit": "2a300c0bb8b5814b231f89ef551665ae5c3b27b9"
11
+ "commit": "968353bc05afbb23453d56add2eabf2d31220fb0"
12
12
  }
@@ -16,7 +16,7 @@ second copy is custom/camera_rig_2/.
16
16
  ## When to use
17
17
 
18
18
  Any game played on a board, arena or kitchen the player should see whole:
19
- Overcooked-style co-op, party games, top-down shooters, puzzle rooms, a
19
+ co-op cooking, party games, top-down shooters, puzzle rooms, a
20
20
  mini-golf hole. The default third-person camera trails the avatar and hides
21
21
  most of such a play area; place one Camera Rig over it instead of writing
22
22
  VirtualCamera code.
@@ -34,7 +34,7 @@ The rig's own Transform is the setup — no code, no names to wire:
34
34
  side, tell them to turn the rig with the rotate gizmo.
35
35
 
36
36
  Params, set in the placePrefab request:
37
- - mode: 'isometric' (default; angled, the Overcooked look) | 'top-down'
37
+ - mode: 'isometric' (default; angled, the co-op cooking look) | 'top-down'
38
38
  (straight down, a board-game view) | 'follow' (the same fixed angle, but
39
39
  the camera tracks the player, for areas bigger than one screen).
40
40
  - activeWhen: 'inside this area' (default) | 'always' (the whole scene, as
@@ -9,11 +9,15 @@
9
9
  "isometric",
10
10
  "view"
11
11
  ],
12
- "version": "1.0.0",
12
+ "version": "1.0.1",
13
13
  "changelog": [
14
14
  {
15
15
  "version": "1.0.0",
16
16
  "notes": "Initial release: a top-down, isometric or follow camera that takes over while a player is inside its area (or all the time), fits the whole area on desktop and phone screens, and hands the default camera back on the way out."
17
+ },
18
+ {
19
+ "version": "1.0.1",
20
+ "notes": "The AI guide names genres in its examples, not a specific game."
17
21
  }
18
22
  ],
19
23
  "origin": {
@@ -1 +1 @@
1
- [{"name":"admin-tools","data":"{\n \"id\": \"14f55d0a-1691-439c-9017-8903afcce3e3\",\n \"name\": \"Admin Tools\",\n \"description\": \"In-world admin panel: moderate players, post announcements and control video screens. Only admins you list can open it.\",\n \"category\": \"custom\",\n \"tags\": [\n \"admin\",\n \"moderation\",\n \"video\",\n \"announcements\"\n ],\n \"version\": \"1.1.0\",\n \"changelog\": [\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The panel stays inside the area the explorer leaves free of its own controls, and the full-screen layer no longer blocks clicks.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release: an in-world panel only the admins you list can open — moderate players, post announcements and control the scene's video screens.\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n },\n \"requiredPermissions\": [\n \"USE_FETCH\",\n \"USE_WEB3_API\",\n \"USE_WEBSOCKET\",\n \"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE\"\n ]\n}\n","files":{"composite.json":{"integrity":"sha384-WYmVW/WiIQ9MIezdTLx0l9KALl8G6IYSnZplhupYGyuk9QOW7GXVM1GBJanG96OX","size":1886},"data.json":{"integrity":"sha384-OTrnY78c8LfxaEXN6NeJIfNscBPi6142qRQp9P9m/+GbCvKw86x/J0faFwW7ERTn","size":927},"icons/admin-panel-control-button.png":{"integrity":"sha384-gs8sS7NlWDosiswd38dlTSRMIxSmPy4uuMH9/Z0G8gWpvKerKgcp3ui7Nme/wbci","size":2217},"icons/admin-panel-moderation-control-button.png":{"integrity":"sha384-mBy3ESO3Mq875LIiSGzFVnD9iLmf9DWMqjEBRnZnT/vmQTy4cendMAM0Pw3iw4I5","size":1631},"icons/admin-panel-rewards-control-button.png":{"integrity":"sha384-lbA1p3+WUiCqMyLQZCtb/GY7G+XGMXvpmsvv6DJWVIV2F4K5G47BJGdJDcuOk9pj","size":1796},"icons/admin-panel-smart-item-control-button.png":{"integrity":"sha384-4dx0rzzVKfDcfpUwMvngJ9THOAclyEDKbUwtTo826cRQOvFez6s78DipQSJt9WMY","size":846},"icons/admin-panel-text-announcement-control-button.png":{"integrity":"sha384-Tp9W3GXQPvd9OwANHFYrfekQpNHdFzcAdRgK0KedviMH4GtXirlh1xNDZivpN7Ii","size":522},"icons/admin-panel-video-control-button.png":{"integrity":"sha384-GR1IO0b/6EbWm9nZwfSm0c6JOfNgxGMC9JmF4+dcI2E47tz5ayJ75fKIn5EdIiZn","size":726},"icons/admin-tool-background.png":{"integrity":"sha384-I87kdzFsmxJLWQNdkQAJK7ech9hyy/I5eA/LbF8+O8R+mQF/+PHTOjGoEgqnDD+M","size":792},"icons/announcements/text-announcement-chat-message.png":{"integrity":"sha384-Wamzz2shgywHVGOsvtruJrW6pEJ8BA4wLopI9m6Aj3QfLpS6+JIhVd/gQW1BeHQz","size":14307},"icons/announcements/text-announcement-check.png":{"integrity":"sha384-ocqK42WSlHJF+8PKK8yUlxlgLaq98LHnmrksUDGjZY/griiuV0QTW4LjC9N5ibj7","size":283},"icons/announcements/text-announcement-close-button.png":{"integrity":"sha384-2/dJo7RNlVSyWFmyM8PI7k2Mn7NbpTrhugMCn4b6+doaqJa83i6V1q0Lft19vMj6","size":1148},"icons/moderation-control-icon.png":{"integrity":"sha384-7A2sHB+ce6HTmfiULIsEQp/N+UZdf3tivJui0rNjghcCka7n7eSfWgl4fjvz4Cgn","size":1312},"icons/moderation/admin-panel-verified-user.png":{"integrity":"sha384-vNcq6YpNNQ7lFotU8SaOCKBhil8PLzDWsblH357op7HxAA2/M6Rg0Vj/HQzpnbZI","size":1213},"icons/moderation/ban.png":{"integrity":"sha384-cmN14nYErcvzkNCovoKGXocGHo7KGrn6R+k91cdCG/wIXtvr3RNim70WTj7rNBeN","size":470},"icons/moderation/chevron-back.png":{"integrity":"sha384-mqnl54KA63yVGjEnCmpO4OP5d0P1Yn6mKr+vBskc0k6X195fA2sby0tLRf1U/rh+","size":3165},"icons/moderation/chevron-forward.png":{"integrity":"sha384-rLe+qxxpQqYMMynmmpvQtFf7721X2H/dqLeqpe3O/PQ3GoUBU9gPU2+VrLoXbFxm","size":3171},"icons/moderation/close.png":{"integrity":"sha384-3MgIzHRKOzSovLyjR7BqmW+CDECeCtQP+qgwJYp7DyG6UncrCp69WwDmQFrAPAab","size":3851},"icons/moderation/error.png":{"integrity":"sha384-UaTdOECxl6EaoYfijFAVBut8YPdUyMQLeRibiNc1Zgl7icEN2xEXrar8IGahlkb8","size":798},"icons/moderation/person-outline.png":{"integrity":"sha384-SpgAMD/+hqIDVz9fx+47lsGoP5R04k76Ld7Vsjt/avZBqVRhfq9+1i/2cXKLYgm+","size":1085},"icons/rewards-control.png":{"integrity":"sha384-o16Vo2/34yoPpO5eaLkz2PsbfOsN6d0OKKCAiYai4QfHN3cSi6WAUJ2BNDsYcrYu","size":785},"icons/rewards/close.png":{"integrity":"sha384-3MgIzHRKOzSovLyjR7BqmW+CDECeCtQP+qgwJYp7DyG6UncrCp69WwDmQFrAPAab","size":3851},"icons/rewards/send.png":{"integrity":"sha384-S33P3X184uAhwTQAeuj9xsMUNXbZ+u/NQTEfFGBA7T0U+AJgGEf7fF++U43aoT1p","size":500},"icons/smart-item-control.png":{"integrity":"sha384-ifsViL2s+VFP4zoJYSRnK3YbSC/CHaJip0TZ+t21bWNVl70IbI1HKlCfdeW2UDSg","size":496},"icons/text-announcement-control.png":{"integrity":"sha384-7URWvpEp3gmP6yJ2hO0TRwMoGwneSaTftS0G8oJJkxEDfLuAFC0v3e5svRbe06s3","size":454},"icons/video-control.png":{"integrity":"sha384-08i8/ot11Lq7VQmc0m2GEqXsE7SydKZBg3zgTAlFEWUFw17F9+agTncc8jPekmSl","size":643},"icons/video/chevron-down.png":{"integrity":"sha384-OcKDCPWWVFuK9NILpIRUhFTh39zgaew6mFlyV7T40mJrBJXr2Q1p4/xABVEplRnf","size":343},"icons/video/chevron-up.png":{"integrity":"sha384-yX6sXRFGFknZtfhtuXNUJr3lc0DgnN8K3y2gPoHbowu9bbkdme0TxDU4/UQh9hfQ","size":346},"icons/video/copy-to-clipboard.png":{"integrity":"sha384-eyjGkM9EtomLolYvPV/xK39hFFazZIwtzd/XF34srRydJTSp2bxS3UafHk+/cP7D","size":304},"icons/video/error.png":{"integrity":"sha384-UaTdOECxl6EaoYfijFAVBut8YPdUyMQLeRibiNc1Zgl7icEN2xEXrar8IGahlkb8","size":798},"icons/video/eye-off.png":{"integrity":"sha384-xQXGDg/WIuy/UdZlYzBuwUY/d1Qp0wfSiavWmAbe5Jzs2+x2O2+qzc+wQyvpY5eS","size":9468},"icons/video/eye.png":{"integrity":"sha384-wwXqHg8tIOK/X1H/xf+6d3GWRtObvrgE553mA8H0RX05t+LFegaRQ+oekZNhfzUb","size":9696},"icons/video/help.png":{"integrity":"sha384-wur+rnzxUR5aoo/HZGtbcRtH46TTEe51XrORkzXpI6GJEzgxe26v1wB4dqUMl6rY","size":6353},"icons/video/info.png":{"integrity":"sha384-2VAiLc08J0wHLo6rbMq0dryIHmXyIwxmz+QLGvOy4CA/FoVwUw7QcxKkJdWy389l","size":37460},"icons/video/person-outline.png":{"integrity":"sha384-SpgAMD/+hqIDVz9fx+47lsGoP5R04k76Ld7Vsjt/avZBqVRhfq9+1i/2cXKLYgm+","size":1085},"icons/video/star.png":{"integrity":"sha384-9GFGGyegeJD8W7mQETsKMaajbcVwTftNidPsCZ4ZdguEqe4e7ApgMaWbbQK3Wq5q","size":612},"icons/video/video-control-dcl-cast.png":{"integrity":"sha384-jfetm7VqkoaYFM60cRDbHYFUKV9PFYdoSJoJJjnlmUMcHXJaXMdqf8CEsfTbfiW0","size":3958},"icons/video/video-control-live.png":{"integrity":"sha384-zKG2r2mRC2+KaBSuCOr8GXCooPlhdS0DM/bhkKuJcZESHLGHUmIKqKCSpBQrTtAB","size":12998},"icons/video/video-control-loop.png":{"integrity":"sha384-PmW7BM5QYFSdD7yWHRAM2ENhpXqabDVBMBraqbQZQX+FSgtzQkM+5sxOvzdnploj","size":8685},"icons/video/video-control-mute.png":{"integrity":"sha384-UNYvZ+/8Gxof8muaqCDw/PIz7H3tLC3j5WyD3vBQvf5JBnQEd0QCIlr+T24HlYPW","size":5752},"icons/video/video-control-pause-button.png":{"integrity":"sha384-cvfT4+N9agDRnU6Ow9wT+Ronr/RJx1c2CAaAt4t3xHPQLvWOJNnPlfXW/O0hyYus","size":5267},"icons/video/video-control-play-button.png":{"integrity":"sha384-CAvkQGDWHS1tYlEA/z33gKf9mvIXPAsISRbl3pdzT52NN6eF/woOQJ+ihnflI7pd","size":402},"icons/video/video-control-video-icon.png":{"integrity":"sha384-yKtdqQ/8ARbNu6EXx2DO479v9R89Qf/gkYmwT9Srj6Olr2qZom3S/TAhJSmR3Ufu","size":667},"icons/video/video-control-volume-minus-button.png":{"integrity":"sha384-P2EM6fgmDXOFDFdfWKYbfUS3/j1pIXgYe6SYkvxhOkLcVNkvDc5rM/P60COE64QP","size":188},"icons/video/video-control-volume-plus-button.png":{"integrity":"sha384-lBnQMGgUEbRTddesn4aY7SxDQ07mz3UKlB9hcwwbfJ1sO215UNs1cXIuIeGfJ4we","size":267},"scripts/actions.ts":{"integrity":"sha384-hE/4GuMatbDddD196YnJeLlpA57vKLHYTS5eRqrChRbpKSWYlfD+d69W4AWD7K52","size":7017},"scripts/admin.tsx":{"integrity":"sha384-Ej24LfV0mpU0RNLnAlt1nWWrDhrbpVFQQUu6gnPt4EO1k0IlvKz3cAfdA6vHzHfM","size":7868},"scripts/api.ts":{"integrity":"sha384-dhPp6JRdztKLmBTidsgYNe69Y/ZEFwDc73blrCgLgRtkgq6kByvEusNYrpKR8gCf","size":4464},"scripts/components.ts":{"integrity":"sha384-OvrIEnNGEwWpatTyqapsXXniSDfxjgtBFx0Zb8J/yGxaEl9JGg9j+e1JTcKywqu8","size":5953},"scripts/icons.ts":{"integrity":"sha384-W7so7VfFuLEVUMJGNxWA2kNXw5LPKL0D4ZkB+WU0pBp6fkM6NHwYDZHRaS3S4Gji","size":2306},"scripts/message-bus.ts":{"integrity":"sha384-BxGE3lQmg0OUPQpVtv7zQxnFG1AphUU2RpRcT0CaiIBduAZ+hpExXvd9gSj0dDkp","size":10293},"scripts/state.ts":{"integrity":"sha384-vNTXekH1jMcqiMauLtz6bZ2Yy5v8qwO66Jf0h6q+LIfGEhKVQsYPIgN/9i55bFlq","size":2000},"scripts/tabs/Moderation.tsx":{"integrity":"sha384-UsU9Wai6aff/kpLGiXtZAyHSEurtjb+B/umwgPRkYLmnqtQ2iSyeHrV2NKXg+Mlc","size":4717},"scripts/tabs/Rewards.tsx":{"integrity":"sha384-bKzO+bKEXJSdq3cDap8pn4KiCgm1gzqmf3Ei4ydcIrXCZA2KHmK7MeyGtQaOnlYP","size":4845},"scripts/tabs/SmartItemActions.tsx":{"integrity":"sha384-2EBxPB8FoidC82/EJIbfdj0WLZY9EM8DKIMfY6WcNkIJNazziDoT0c9TWLDQSgUu","size":6393},"scripts/tabs/TextAnnouncements.tsx":{"integrity":"sha384-auE1OM8qu77Mf+NJ0Q9phmWHoa/8hAELiRNzk0iMzh9p9P++K17e2r6n4wRu8V4q","size":6297},"scripts/tabs/VideoControl.tsx":{"integrity":"sha384-kb3f5pujq+a7c32PQSwUEOEPRfdjabLpJXnYsDCzDOepMmSPpIRpQ5ZEkbCDRajd","size":5199},"scripts/tabs/announcements/Overlay.tsx":{"integrity":"sha384-gwqwL5sPpKVyrX5zvXFl+spo+hOQuZ5Ao5HtwlcqNNjAszJtbZQZkRGoIeS91yQr","size":2933},"scripts/tabs/announcements/icons.ts":{"integrity":"sha384-ITtseCBy5n3WQxScQG0Y6LxNaD/L8CTPmI/lboI4QTMunXwHpmFmkW4SKHOgUnBn","size":455},"scripts/tabs/announcements/state.ts":{"integrity":"sha384-H8QR1U5I52xzN8kBd0JoMNmVPDXk+9h+G4Ophfhb413JPQgGUqva1XR1BC/ck8AN","size":2351},"scripts/tabs/announcements/ui.tsx":{"integrity":"sha384-PuNRzeUXeOVMuBO/sXnBrGEaB8n3qPofhnJOtC5FXz6EfnQ1T9wS63XPRa23GKRU","size":1072},"scripts/tabs/moderation/AddUserInput.tsx":{"integrity":"sha384-RiyCa08HtzCBd3VEe+tP6ntPOzHyb1F98RhXqkAwBKB14OT9puqedXekff7aGs37","size":5617},"scripts/tabs/moderation/RemoveAdminConfirmation.tsx":{"integrity":"sha384-tT43wHl1ywHy7tmio8nqzeZdr8kymxwHUfaPHoVc62u7ODx9GCygB4pu8jtHPRiX","size":2803},"scripts/tabs/moderation/UsersList.tsx":{"integrity":"sha384-0Wa4fy2crAQe39qgwbwdBwQSFswNGCrELHxCFZ1jBmxgh4R1HYKKDrFpAwtoq87/","size":7191},"scripts/tabs/moderation/api.ts":{"integrity":"sha384-Q/qkDk6GC53eutQvYwf4Lmyy3fPEH84nj1c+7JZDYEt0xrmGHgYPcXcfju2bWNii","size":1322},"scripts/tabs/moderation/icons.ts":{"integrity":"sha384-QqvX+zxVEVZojv5uHj0yl9aaSO3dPX8g7bvAizI7+jmUfT7Y919Bu6m1lbM33FYC","size":706},"scripts/tabs/moderation/kick.ts":{"integrity":"sha384-Rgr5yEmwEtTgZQirGmAOjGlJ2S9eBk35RwCgLTnkfvIQL4Q+ueU77sJ1nkeyPjfL","size":3965},"scripts/tabs/moderation/state.ts":{"integrity":"sha384-xvW2sWrMBcIr395zhA4Z/CJmvRzdtUJYIwJj4jBO0BcqYtaokEtRInseFPyfWU5A","size":1950},"scripts/tabs/moderation/ui.tsx":{"integrity":"sha384-s/X9vWTUhAfIZbzFtFwAJsjBjtBx7WcNpYOV1X5a2aruCkBB9zGqsRGF+k8ieVfW","size":5207},"scripts/tabs/moderation/utils.ts":{"integrity":"sha384-TAcs4ANF4RdzECa13ol7kWSKBtNZZZ5n+IydZlt9vl0FqZhnaw+MiBD2Q8gtacsU","size":3184},"scripts/tabs/rewards/Button.tsx":{"integrity":"sha384-Z66D7boMToX9iMvJr2cCAvA6EuxSPNKAMnIMeyry+xnokDOenJrmDaVBqX2pN6gt","size":1539},"scripts/tabs/rewards/Captcha.tsx":{"integrity":"sha384-H89W2CtQmpxF4LU6hLh409aHbadDb/Ukv5oRVc65e6kI+C7ixvCXPDtBjlerFnyo","size":2490},"scripts/tabs/rewards/api.ts":{"integrity":"sha384-T42JhF83QZuzIB06HAa09Bclq/qUwRvP/FwxhhAaVjuH7Ml0ehmiHgZDRYj95H5S","size":4796},"scripts/tabs/rewards/claim.ts":{"integrity":"sha384-pRPeSNGiGx9wb4+v35haHCGfWX+CBiMLu/1iVLitZ6pNhTzvBu24CisrFWmycQqM","size":3656},"scripts/tabs/rewards/controller.ts":{"integrity":"sha384-JPs8MvAVa6pGrdKlvoFr0H1P3FoQMIuiwwGcZMvkHC8kVVq4saX0x5T4RCIH35Rq","size":3368},"scripts/tabs/rewards/icons.ts":{"integrity":"sha384-S/7TaZ0mzvxhZJnxzt2ilqYAINoHo81Wh2vLRELZpmS/jQuL6KGBG9Tmw+COF1pb","size":275},"scripts/tabs/rewards/state.ts":{"integrity":"sha384-4PsbphYBjuf6o9Dnz8h6Iyp+s1LQrV1PYhteV0zpHz3nQFo8mZf8gRbphB+fpEdT","size":840},"scripts/tabs/smart-items/selection.ts":{"integrity":"sha384-lxSzgZrpbsomjarF9K1UhsfX1QuiPmeqMPY3jUBOG2/CU9i9ZCXrRPAexlhOaNiv","size":3331},"scripts/tabs/video/DclCast.tsx":{"integrity":"sha384-Rz/xL437EcTxwNCYvjbuHuw3klhBGL6N/Pb0hc6Vu+gjJVrLKnqRIo9hTEzeqWs6","size":8072},"scripts/tabs/video/LiveStream.tsx":{"integrity":"sha384-xJfNS7uAkczd6Mv7jI6kfVhA4cJQfbzUXZB+POKe7scklnpJJttNimcHHfl2sh5P","size":9886},"scripts/tabs/video/SpeakerShowcase.tsx":{"integrity":"sha384-5fEeVnMIEMYTQHztNlyULWEts6h/Zgv3kH5+/iQy9uIh5eNe1iqVtwArKrM0RuAP","size":3802},"scripts/tabs/video/VideoUrl.tsx":{"integrity":"sha384-pEnlEeG6qstYzljbAkeeFfQZilijueSfKmZFUPOzDC8I6K0ETrlf45hbbMX8V0fr","size":3426},"scripts/tabs/video/Volume.tsx":{"integrity":"sha384-q4odOzbD+Mx5BzotjtIx8+GQMGX9Ysy657573+S/bts+yrr1w0bMN91NtKzNc7Vf","size":2133},"scripts/tabs/video/api.ts":{"integrity":"sha384-UDwFd8lR+diOPOejRUq8Bk/DHs1OQN9nb7jNE2Nv+1KUm4JwUxu4b7LMExuWDSuJ","size":5673},"scripts/tabs/video/controls.ts":{"integrity":"sha384-8NTX/Mu0Ps34x4kxL2gwmJrbxyUVxYwT0pH5ig22+FGJU6UqHKIV2MyPZbJyBEWM","size":3886},"scripts/tabs/video/icons.ts":{"integrity":"sha384-3/0EHH0ZB0R4gcxXUlHvWFqKEoHx0WJyl7ZWh3sAGpIYGcJFT4OtqzeUkA0gRa4X","size":1662},"scripts/tabs/video/speakers.ts":{"integrity":"sha384-6YGpbWXwW6pSBAHC8o9P2Lu0P6IbhsRseHAgDG2eHWUCFd4wAXhdR6l/FYM7wrL4","size":1105},"scripts/tabs/video/state.ts":{"integrity":"sha384-yPWXz+LaGfDLrbO6xuEAe6whwGGtahVvvpOJmQsLGc+P4qopgoD9FSA9+xOxZP8I","size":2256},"scripts/tabs/video/ui.tsx":{"integrity":"sha384-ANXvEZ75Ur0nZyxxaHFOdr+NBGx3zD25r3s8M8dVb6osakUGjBKhRUEEWKo6JmpV","size":7076},"scripts/types.ts":{"integrity":"sha384-/ieszyQ/vz9IKpyeCLEw7Gae+8EWZVQMkcWmKWn6roV3KkEj34zY8n87R5w9HxIY","size":1902},"scripts/ui.tsx":{"integrity":"sha384-fUQUBEHzEB8SO6Mhl3Trm9vfgCogyEIoOiVmP+eVnE1qI/HK6lqOFsLcNZNe/Lqy","size":3216},"thumbnail.png":{"integrity":"sha384-sh8ip9U6LNItjm30RSrxl83x7aYdlbROEDnI49S5x+uj3rpx7ohWSFgVi3jXpv+4","size":11615}}},{"name":"announcer","data":"{\n \"id\": \"fb41d8d1-b842-437d-a830-f38379e74743\",\n \"name\": \"Announcer\",\n \"description\": \"Show a themed message when a player enters or leaves a chosen Trigger Area, or receive announcements from Game Flow.\",\n \"category\": \"custom\",\n \"group\": \"Multiplayer Server\",\n \"tags\": [\n \"multiplayer\",\n \"server\",\n \"announce\",\n \"message\",\n \"toast\",\n \"ui\"\n ],\n \"requiresSdk\": \"auth-server\",\n \"version\": \"1.3.0\",\n \"changelog\": [\n {\n \"version\": \"1.3.0\",\n \"notes\": \"Announcements take their place in the shared HUD layout at the bottom centre, narrow enough to stay clear of the phone touch buttons and of other HUDs.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release: server code anywhere in the scene calls game.broadcast('announce', { text }) and every player's client shows the line for a few seconds.\"\n },\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The placed item's Script card now shows the broadcast line that makes it speak, under its hold seconds and font size.\"\n },\n {\n \"version\": \"1.2.0\",\n \"notes\": \"Shared theme and a Trigger Area picker for local messages without writing code.\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n }\n}\n","files":{"ai.md":{"integrity":"sha384-D3Cv9CWg7ofWd+5NtZB9U0GLWnX/7Mbx2KN4QumC6l0qNu9t/6EXIVVuVJcY/7NS","size":3331},"composite.json":{"integrity":"sha384-3w+7KNTh3KLidBVPFpOr5uXPyrtZH1UA5l5yy+r0WC4QYO8+bFmvO10wY6Zk1osX","size":554},"data.json":{"integrity":"sha384-UiHAWkFaSxgHUM3ufcszXLhIXvc/PRRv7y2VMpJdBHNJ8C8Bzw7Yk40k9dW9Sabt","size":1217},"scripts/announcer.tsx":{"integrity":"sha384-cXTJST1CbTriSs6Vjg5CMhMzuGYX8Y5um8dH04rhrBrpMGI3/+rp7rF+OyiK6Bjs","size":1761},"scripts/pure/toast.ts":{"integrity":"sha384-5HsNX5Ao3DBwh0XwQx036b4r8OIvM4C7zYUAPHMVNPtEWb7kMdJASFEwKtxrb/jE","size":1171},"thumbnail.png":{"integrity":"sha384-HBZUcCEOa9jkubVyvd8Hrhnu3HU0OHKhshGy1LYXw0I6bANVnxcUy0psn8fuyqR6","size":2005}}},{"name":"black-chair","data":"{\n \"id\": \"1aa01433-9f2e-44ac-b1fb-a2bdffeb6776\",\n \"name\": \"Black Chair\",\n \"description\": \"A black dining chair. Players can sit on it.\",\n \"category\": \"custom\",\n \"tags\": [\n \"seat\",\n \"furniture\",\n \"sit\"\n ],\n \"version\": \"1.1.0\",\n \"changelog\": [\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The seat no longer runs on the Multiplayer Server, where it was walking every sit spot's transform chain each frame for nobody to see.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n },\n \"requiredPermissions\": [\n \"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE\",\n \"ALLOW_TO_TRIGGER_AVATAR_EMOTE\"\n ],\n \"group\": \"Seats\"\n}\n","files":{"HighBackChair.glb":{"integrity":"sha384-BQAU/CeS2nc/qMg4zNmGfN9PL54Vok9qKD681gVfC64knQyWW/wh2rGXNVc53oJ6","size":32424},"composite.json":{"integrity":"sha384-3/TVCIpJOzxplhEyfQhTIfMqAzc6J/jyo6qWRCI3iQMMSlSNnYS69w8+vBdNheC2","size":1413},"data.json":{"integrity":"sha384-zBCFwVRiankPHLu/0p80cZPNHuuPr8KywXQh+Co6MN1LWmPfYffNk2UvpyFc3WK2","size":699},"scripts/seat.ts":{"integrity":"sha384-qTc33dk7kx9+avQ4JugIv1csCyESSIOVst3FKQhN3pC0OIyeh/yA32h0z8zjdIWc","size":6959},"thumbnail.png":{"integrity":"sha384-bZsSYvZkaq1R6jGe8fK1yvdC1ws8F0nAtbKUpWRkpEretquQ1Pcx05H14NIobZA4","size":8744}}},{"name":"camera-rig","data":"{\n \"id\": \"7ea892f3-168f-40fb-8338-d9c91c23708e\",\n \"name\": \"Camera Rig\",\n \"description\": \"A fixed game camera — top-down, isometric or following the player — that takes over while a player is inside its area and frames the whole area on any screen, phones included.\",\n \"category\": \"custom\",\n \"tags\": [\n \"camera\",\n \"top-down\",\n \"isometric\",\n \"view\"\n ],\n \"version\": \"1.0.0\",\n \"changelog\": [\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release: a top-down, isometric or follow camera that takes over while a player is inside its area (or all the time), fits the whole area on desktop and phone screens, and hands the default camera back on the way out.\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n }\n}\n","files":{"ai.md":{"integrity":"sha384-9x73whqmr1qGQuEfpGqJgQdFYwZ4mvb0z2siWJEDgaCAU+qn18Dty0o9x9vHvKha","size":3869},"composite.json":{"integrity":"sha384-ou9CI0Q1Mt9ikRHU0UUDqqrC1Y3E4dv/vSlrYzb2d9SZbesrFlrit1ulk0D/gJDT","size":915},"data.json":{"integrity":"sha384-siOIwgynLDVrAOmq+E6fjUJdUWxyGruViMlGQCbIW6fB69RRfj6dA/QCQ2m0/fbL","size":741},"scripts/camera-rig.ts":{"integrity":"sha384-S/ZnswWj9Wh/IMJ6RL84LmU6H6B0JLLRB6mRB2ucG3unU2jAMBVHklNp0z4vZ7iA","size":7767},"thumbnail.png":{"integrity":"sha384-jkkA8fFCJKFnzNRruLLm6Snhpa/+0i224sx+HrpzEd6JGw1+fzITRRXnFyn0lSO3","size":16703}}},{"name":"classic-bench","data":"{\n \"id\": \"3e8dace7-01e4-4a98-a56e-52ad05ae654d\",\n \"name\": \"Classic Bench\",\n \"description\": \"A classic park bench, seating two. Players can sit on it.\",\n \"category\": \"custom\",\n \"tags\": [\n \"seat\",\n \"furniture\",\n \"sit\"\n ],\n \"version\": \"1.1.0\",\n \"changelog\": [\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The seat no longer runs on the Multiplayer Server, where it was walking every sit spot's transform chain each frame for nobody to see.\"\n },\n {\n \"version\": \"1.0.1\",\n \"notes\": \"Ships the model's texture, which was missing from the folder — without it the model rendered blank white in-world.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n },\n \"requiredPermissions\": [\n \"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE\",\n \"ALLOW_TO_TRIGGER_AVATAR_EMOTE\"\n ],\n \"group\": \"Seats\"\n}\n","files":{"Bench_01.glb":{"integrity":"sha384-qU+pwMwPJYsOMD3Rgh5aQ/XkBKR6ls6nQyqWaRik+z125N+cSW7f7tfaH6rQPVjz","size":54436},"composite.json":{"integrity":"sha384-KTMuUiX7SNG/qh3LQ9JANv/ql+YrUC3UHna5xa8GleFBEVOx2NTafDEFyjXPlUZL","size":2004},"data.json":{"integrity":"sha384-WzR61MlY+E4phm2i405fbw1qr2W5XZhV3elr6lpAIVX7ssoqTzxb8m3lTSKaqrLi","size":887},"file1.png":{"integrity":"sha384-hkHg23U+9Yx8eU+7OWRgOsHJ1gS8nzdWZ/DEAQvtwJF3dOf7sTQwINVaIo30FRk5","size":7117},"scripts/seat.ts":{"integrity":"sha384-qTc33dk7kx9+avQ4JugIv1csCyESSIOVst3FKQhN3pC0OIyeh/yA32h0z8zjdIWc","size":6959},"thumbnail.png":{"integrity":"sha384-gVZ5hbkqeZCkqlNp2XTBqGLqDMFBH261RyXCV/+xogCBddr593SpTijJcTG75xRw","size":10722}}},{"name":"classic-bench-armrests","data":"{\n \"id\": \"88e01dcc-b242-4815-90e3-72ff52c24f45\",\n \"name\": \"Classic Bench Armrests\",\n \"description\": \"A classic park bench with armrests, seating two. Players can sit on it.\",\n \"category\": \"custom\",\n \"tags\": [\n \"seat\",\n \"furniture\",\n \"sit\"\n ],\n \"version\": \"1.1.0\",\n \"changelog\": [\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The seat no longer runs on the Multiplayer Server, where it was walking every sit spot's transform chain each frame for nobody to see.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n },\n \"requiredPermissions\": [\n \"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE\",\n \"ALLOW_TO_TRIGGER_AVATAR_EMOTE\"\n ],\n \"group\": \"Seats\"\n}\n","files":{"Bench 1.glb":{"integrity":"sha384-gEvudQhcSvOjXcqAeY8D8iPaVbsYOkBd3YdZki2iAKCkZgkf/PQLaMW5DAJHFVZB","size":42772},"composite.json":{"integrity":"sha384-KuygG/7cDAvXqt2lz2mYDyeppiq1PZN+ZmBDndaWxdqV7csCPMdXQK/Ys40UW7YZ","size":2014},"data.json":{"integrity":"sha384-t19LA1qyWZc5sRylViJkgGHA8wo0wZqEVaA586BsRGQmzQc4itLDkgxE4LIdvHeR","size":737},"scripts/seat.ts":{"integrity":"sha384-qTc33dk7kx9+avQ4JugIv1csCyESSIOVst3FKQhN3pC0OIyeh/yA32h0z8zjdIWc","size":6959},"thumbnail.png":{"integrity":"sha384-u4mWUcX2kkrcgfIKydVN3kKeDu7K2Jfg9TKNBYavhhiIjZ0bWSUPVg9VABZZuaV1","size":19258}}},{"name":"classroom-chair","data":"{\n \"id\": \"cacd50dc-5861-4b71-83ee-83cb23fea9e4\",\n \"name\": \"Classroom Chair\",\n \"description\": \"A moulded classroom chair. Players can sit on it.\",\n \"category\": \"custom\",\n \"tags\": [\n \"seat\",\n \"furniture\",\n \"sit\"\n ],\n \"version\": \"1.1.0\",\n \"changelog\": [\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The seat no longer runs on the Multiplayer Server, where it was walking every sit spot's transform chain each frame for nobody to see.\"\n },\n {\n \"version\": \"1.0.1\",\n \"notes\": \"Ships the model's texture, which was missing from the folder — without it the model rendered blank white in-world.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n },\n \"requiredPermissions\": [\n \"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE\",\n \"ALLOW_TO_TRIGGER_AVATAR_EMOTE\"\n ],\n \"group\": \"Seats\"\n}\n","files":{"Chair_02.glb":{"integrity":"sha384-4sbNyI8nx8fgdkKm/Df7ooV8fEtA5sUTxy7VI2R+91f/JDklg4yFxOk4U3RXIFuo","size":16368},"composite.json":{"integrity":"sha384-Esh68GGVhUf9RCOfsDXWQaUiGb273cV7Z5cC2MOXe6JkUJknpHCpvyBwgPjvIHTg","size":1447},"data.json":{"integrity":"sha384-2FqLS4bQ26dRzDJZgb3HlB/Hrlt6x8fzSQnCoHVeGFIyZHcJ77N4WClJlt0iAYQB","size":881},"file1.png":{"integrity":"sha384-hkHg23U+9Yx8eU+7OWRgOsHJ1gS8nzdWZ/DEAQvtwJF3dOf7sTQwINVaIo30FRk5","size":7117},"scripts/seat.ts":{"integrity":"sha384-qTc33dk7kx9+avQ4JugIv1csCyESSIOVst3FKQhN3pC0OIyeh/yA32h0z8zjdIWc","size":6959},"thumbnail.png":{"integrity":"sha384-xp435rb3UtVrmBql5h82OUHv9+1HRv4msRquMkJF1ABAwAmE0htpneGPsIovG1dU","size":14671}}},{"name":"court-chair","data":"{\n \"id\": \"e29fe7dd-20f3-4486-941e-bb85401d0d1d\",\n \"name\": \"Court Chair\",\n \"description\": \"An upright court chair. Players can sit on it.\",\n \"category\": \"custom\",\n \"tags\": [\n \"seat\",\n \"furniture\",\n \"sit\"\n ],\n \"version\": \"1.1.0\",\n \"changelog\": [\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The seat no longer runs on the Multiplayer Server, where it was walking every sit spot's transform chain each frame for nobody to see.\"\n },\n {\n \"version\": \"1.0.1\",\n \"notes\": \"Ships the model's texture, which was missing from the folder — without it the model rendered blank white in-world.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n },\n \"requiredPermissions\": [\n \"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE\",\n \"ALLOW_TO_TRIGGER_AVATAR_EMOTE\"\n ],\n \"group\": \"Seats\"\n}\n","files":{"Chair_Wood_01.glb":{"integrity":"sha384-IIv1LpNQXbn42cpCwd4gsgDfThllJfAjB0pjMC51jcgUqJD6yJ25vIUv4OUSNZ0B","size":38776},"FanstasyPack_TX.png":{"integrity":"sha384-KMdE2Mz2QPnFyTWlJZWCUSxASSMsmyw/VFzHLyFQvXehN1ujIEepbskPuNqHgCI+","size":15078},"composite.json":{"integrity":"sha384-Q1IbQdubZR+c8womUaRcMywMD5GjYB/t46kuu309jIHimWA2YHGNgl/Ei5G/B1Bg","size":1408},"data.json":{"integrity":"sha384-akBy4ls/agEp39hMybq9vUIOyxV4KMBId+YLlayMWgc7QJcI/cLc+UktiHJOny6R","size":874},"scripts/seat.ts":{"integrity":"sha384-qTc33dk7kx9+avQ4JugIv1csCyESSIOVst3FKQhN3pC0OIyeh/yA32h0z8zjdIWc","size":6959},"thumbnail.png":{"integrity":"sha384-v2OuF/sRj8TMvjUqhYYJTkwfAMftvT//lNEFTULD99DFiWfc/bWhceAxxeo7emX+","size":12243}}},{"name":"curved-couch","data":"{\n \"id\": \"48dbe32d-c906-4287-bcfe-2cfe280c68f8\",\n \"name\": \"Curved Couch\",\n \"description\": \"A curved sectional couch, seating three. Players can sit along it.\",\n \"category\": \"custom\",\n \"tags\": [\n \"seat\",\n \"furniture\",\n \"sit\"\n ],\n \"version\": \"1.1.0\",\n \"changelog\": [\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The seat no longer runs on the Multiplayer Server, where it was walking every sit spot's transform chain each frame for nobody to see.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n },\n \"requiredPermissions\": [\n \"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE\",\n \"ALLOW_TO_TRIGGER_AVATAR_EMOTE\"\n ],\n \"group\": \"Seats\"\n}\n","files":{"CurvedCouch.glb":{"integrity":"sha384-EnJVY5fVNrynT2dL7huvsxVfZ5RbbvmrxnGjrEobubreqJ1U7qQo1ofzwBcNGYgg","size":38740},"composite.json":{"integrity":"sha384-Mx4dSJiZ3vo9xNctXyshiVZNlQ6SaYKDrtdLqpRT+ODzyJGneMwOxA/64u2rfqre","size":2013},"data.json":{"integrity":"sha384-7TJ7cPkU1KWPclbaj5CKLkVvIM6Ascfewj4ikqy9H6KRpu26ZyaBZfE6UekpP/ZU","size":722},"scripts/seat.ts":{"integrity":"sha384-qTc33dk7kx9+avQ4JugIv1csCyESSIOVst3FKQhN3pC0OIyeh/yA32h0z8zjdIWc","size":6959},"thumbnail.png":{"integrity":"sha384-oyKNtE22C6BjtcNTnnoBTYjfmIqVnik0SaM5IwfC1qheKHjXVa8Lxtihi9YDu8fs","size":14388}}},{"name":"game-flow","data":"{\n \"id\": \"44374100-12fb-481f-90ce-2a4d81949084\",\n \"name\": \"Game Flow\",\n \"description\": \"Lobby, countdown, rounds, winners — runs your game from start to end. Works with: Leaderboard, Announcer.\",\n \"category\": \"custom\",\n \"group\": \"Multiplayer Server\",\n \"tags\": [\n \"multiplayer\",\n \"server\",\n \"round\",\n \"lobby\",\n \"countdown\",\n \"timer\",\n \"winners\"\n ],\n \"requiresSdk\": \"auth-server\",\n \"version\": \"1.2.0\",\n \"changelog\": [\n {\n \"version\": \"1.2.0\",\n \"notes\": \"The 2D status panel takes its place in the shared HUD layout, so it no longer overlaps other HUDs on any screen.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release: lobby → round → winners on game.round, a countdown every client derives from one shared deadline, a winners line read from the board key a Leaderboard shows, and a \\\"script\\\" mode where your own game.newRound() ends the round and the round length is only a ceiling.\"\n },\n {\n \"version\": \"1.1.0\",\n \"notes\": \"Optional themed round timer and lobby countdown, configured in the inspector.\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n }\n}\n","files":{"ai.md":{"integrity":"sha384-FSQahckNpE6r400CC5Kw5Fz1uijUfaZ0N2XZBEm+YVMsbRTMfjX5zrlyKJMleUyl","size":4677},"composite.json":{"integrity":"sha384-TXN9bYBu1C79b9fTYJ2GbtOC1J4wjEpJt6rIykdYJTu3HEUw62il/8CIFSljcB4Q","size":932},"data.json":{"integrity":"sha384-cP7iRsJsHCRReEuJXncOHP41mG5PmbXB1BUnN0Kp16cOeLv+USBLanxLKbQNVY+U","size":1143},"scripts/game-flow.ts":{"integrity":"sha384-ALqBHmmmh5DxU2YGmpsDbjJyY3Lxh2rn2GBWGu0dlXOxXdG7h6NzhBlxdbVnkqYp","size":6524},"scripts/pure/flow.ts":{"integrity":"sha384-zEgjgIQS3RzCJ8Tyud8tywm5SmD+FO8WLnFpBkexnFEnmbOoWSb37HUx3X0/DLT3","size":7575},"thumbnail.png":{"integrity":"sha384-ZQHTd8rM4CaE2+qNC5at7B4H3e1ikf1ck9K6GI/TEwn/7WKzf2/9I9sInMvrxy8J","size":13227}}},{"name":"health-respawn","data":"{\n \"id\": \"5cc3b4d7-2a87-4825-9a53-e2b007bcff24\",\n \"name\": \"Health & Respawn\",\n \"description\": \"Players can take damage and respawn. Works with: Game Flow, Trigger Area.\",\n \"category\": \"custom\",\n \"group\": \"Multiplayer Server\",\n \"tags\": [\n \"player\",\n \"health\",\n \"damage\",\n \"respawn\",\n \"multiplayer\",\n \"server\"\n ],\n \"requiresSdk\": \"auth-server\",\n \"requiredPermissions\": [\n \"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE\"\n ],\n \"drivenBy\": {\n \"rule\": \"Nothing takes hit points by itself — the server calls damage() wherever the harm happens, and this item does the dying and the respawn.\",\n \"code\": \"game.onEnterArea('Spikes', (player) => damage(player, 25))\",\n \"next\": \"Press New script below, write that inside the isServer() block it comes with, and import damage from the health.ts beside this item's own script.\"\n },\n \"version\": \"1.3.1\",\n \"changelog\": [\n { \"version\": \"1.3.1\", \"notes\": \"The health bar is on by default again; turn Health Bar off for a game that never deals damage.\" },\n { \"version\": \"1.3.0\", \"notes\": \"Health bar setting, off by default: a course that never deals damage shows no hit points.\" },\n { \"version\": \"1.2.1\", \"notes\": \"Guide covers in-bounds respawn destinations, permissions and course playtesting.\" },\n {\n \"version\": \"1.2.0\",\n \"notes\": \"Per-player checkpoint destinations, healing, forced death, death counts, optional respawn delay and protection after respawning.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release: hit points the server owns in game.state.health, a respawn point picked in the inspector, a \\\"die below height\\\" death plane, and a damage() other server code calls.\"\n },\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The placed item's Script card now shows the damage() call that takes hit points, under its respawn settings.\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n }\n}\n","files":{"ai.md":{"integrity":"sha384-sOrhWgNJh7HnRDNRZnTFtj+lulT6lQpepsjcZ8+8S66IEKIbA/+TR0wineLeGJ0r","size":4755},"composite.json":{"integrity":"sha384-WZskIkQZ8n8dehUBe5OKQ+99aVon0PAX6Znh5z2TmWf177Wto80aE70iod28dvCF","size":565},"data.json":{"integrity":"sha384-/9l/nKLcx51jlRHD0Yafkl9meyPTAwCxTR8XteUkuH5jmmcfa8IK6SIiDJBhoqlp","size":1911},"scripts/health-respawn.ts":{"integrity":"sha384-QwLUhaAcxp9vui+9q7ccvuEpY87QHG+d/pzfAJRPn9L2yHhrvU605ORU21pz17hP","size":5760},"scripts/health.ts":{"integrity":"sha384-/zA9uuvRZ3uRS2WdFvPml83zRvA5n4h0RTNIEd2pb59Ui6yrtX1kJHj/zbK+Md4A","size":1981},"scripts/pure/health.ts":{"integrity":"sha384-if/V7vja1R5enh/H7yM4FCh3Ea7bgld+tr76ryjTWryrk3lEw7xFe6p282WsZFji","size":2230},"scripts/pure/life.ts":{"integrity":"sha384-oNHo7xAdJWVw3t5o7oDt+wjM9ea8FD/Ynskc2l1YjP3+qvnYLNnrI4v1pENPOorD","size":2368},"thumbnail.png":{"integrity":"sha384-fVCccAg9bAVrgYD6MFlGK+1zBO5XEDpxd0Lh7LGFaobZYl2OCaxJcME3eVrQqS31","size":7116}}},{"name":"high-stool","data":"{\n \"id\": \"f8ee0d2a-19f5-45a9-9029-cd46d7a8f834\",\n \"name\": \"High Stool\",\n \"description\": \"A tall bar stool. Players can sit on it.\",\n \"category\": \"custom\",\n \"tags\": [\n \"seat\",\n \"furniture\",\n \"sit\"\n ],\n \"version\": \"1.1.0\",\n \"changelog\": [\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The seat no longer runs on the Multiplayer Server, where it was walking every sit spot's transform chain each frame for nobody to see.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n },\n \"requiredPermissions\": [\n \"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE\",\n \"ALLOW_TO_TRIGGER_AVATAR_EMOTE\"\n ],\n \"group\": \"Seats\"\n}\n","files":{"HighStool.glb":{"integrity":"sha384-pELylPLTKad8MmTwL9+jXPGUf5KOnXgxBuY4JXguuov7duJEIVRk4pRcQ+gCgKmf","size":26332},"composite.json":{"integrity":"sha384-U3fv7ocM1aBZWJ+l9uC2Eg2acDqnoxy3tPhr8dXx1Vi7N1riaugAdQTDIpTQQyAK","size":1416},"data.json":{"integrity":"sha384-LjCLqThIrUN7Qk+zA6paffpTxFsPGIiDdRhVow2w9PlMxyX+EvyLO+tds9fHTX7O","size":694},"scripts/seat.ts":{"integrity":"sha384-qTc33dk7kx9+avQ4JugIv1csCyESSIOVst3FKQhN3pC0OIyeh/yA32h0z8zjdIWc","size":6959},"thumbnail.png":{"integrity":"sha384-Y4cPyTFXuHC5maHoDtEX7vdYSK76XLhw9JbnYPORlyOSmLAfhkvv/FjW0apjV9VS","size":9066}}},{"name":"large-couch","data":"{\n \"id\": \"ac233c16-699e-4f01-8e7c-910bb5a168af\",\n \"name\": \"Large Couch\",\n \"description\": \"A long couch, seating three. Players can sit along it.\",\n \"category\": \"custom\",\n \"tags\": [\n \"seat\",\n \"furniture\",\n \"sit\"\n ],\n \"version\": \"1.1.0\",\n \"changelog\": [\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The seat no longer runs on the Multiplayer Server, where it was walking every sit spot's transform chain each frame for nobody to see.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n },\n \"requiredPermissions\": [\n \"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE\",\n \"ALLOW_TO_TRIGGER_AVATAR_EMOTE\"\n ],\n \"group\": \"Seats\"\n}\n","files":{"ThreeSeater_Couch.glb":{"integrity":"sha384-Oo6LBiZPQO03QYxtyTqaG4NttfLoAeL14jtchqBbJjg1HQftbWUwrE1lQ8mSXCnM","size":31868},"composite.json":{"integrity":"sha384-HtdxTfaa5dyvF9BUlo0t2tzgSA4SWn3nwT2DccLeqnmscPpMqTKKmYt886lQaFYQ","size":2023},"data.json":{"integrity":"sha384-uIPJ6qQK7hXa2HghWSKPj5E8buDUVRb0XdeSUOcYoFnnQFTx3Nn9gWGf9m6kFTnU","size":709},"scripts/seat.ts":{"integrity":"sha384-qTc33dk7kx9+avQ4JugIv1csCyESSIOVst3FKQhN3pC0OIyeh/yA32h0z8zjdIWc","size":6959},"thumbnail.png":{"integrity":"sha384-/JwNvv3M/1qncoi0bQ7ZM9h7WQKP48Ti8KgAdHqtLxiwMogRZmWRhATdgF7nBz+3","size":16512}}},{"name":"leaderboard","data":"{\n \"id\": \"493daede-2afd-4c10-9c0d-6aefb25629a8\",\n \"name\": \"Leaderboard\",\n \"description\": \"Show game scores or automatic Run Progress best times on a 3D panel or themed screen overlay.\",\n \"category\": \"custom\",\n \"group\": \"Multiplayer Server\",\n \"tags\": [\n \"multiplayer\",\n \"server\",\n \"leaderboard\",\n \"score\",\n \"ranking\",\n \"board\"\n ],\n \"requiresSdk\": \"auth-server\",\n \"version\": \"1.6.0\",\n \"changelog\": [\n {\n \"version\": \"1.6.0\",\n \"notes\": \"The on-screen board takes its place in the shared HUD layout, so it no longer overlaps other HUDs, and shows the top 5 on phones, whose screen is a third shorter.\"\n },\n {\n \"version\": \"1.5.0\",\n \"notes\": \"Before the first score the board tells players \\\"No scores yet — be the first!\\\" (or \\\"No times yet\\\" for times) instead of setup instructions, and its text wraps to stay inside the panel. A board placed before this keeps its old text size, where the new line already fits.\"\n },\n {\n \"version\": \"1.4.0\",\n \"notes\": \"The 3D panel's text is readable from its front, and players can no longer walk through the board. A board placed before this keeps its old text position and no collider: delete it and place it again.\"\n },\n {\n \"version\": \"1.3.0\",\n \"notes\": \"Rows show the display name of players in the scene, or a name the row carries; others show a short address.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release: a panel that reads the game.state key you name in boardKey and paints its top rows, so the board a Script keeps is the board every player sees and a late joiner gets it from the snapshot.\"\n },\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The placed panel's Script card now shows the setState line that writes the rows it paints, under its own settings.\"\n },\n {\n \"version\": \"1.2.0\",\n \"notes\": \"Themed screen overlay and automatic persisted best times from Run Progress.\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n }\n}\n","files":{"ai.md":{"integrity":"sha384-mcZT0Ub5OZ/KpfDn6OvJ43I7ITFnl+OVGL7Y/IMznJc6cRuUKOu1VWmqVdyN5+hP","size":4523},"board-panel.glb":{"integrity":"sha384-Su82WkaQqG6obG/yGRkHw+WCXQCX/vMngfKyCV/IUH7tpdqz1MS2777hwjPMNdrI","size":1276},"composite.json":{"integrity":"sha384-sFuc9KWyDZpE2r3m+p3lj2YXw/hlALVOoguXm/wf1fSuN9T5pCXAlD7ow/9gOhQk","size":1916},"data.json":{"integrity":"sha384-TC0KbVHrPpPbhd+YP/5rmdZ7DRp6HfhUbpnEOV/8tiv+/ZPF4bRdzAAch2i36XCM","size":2009},"scripts/leaderboard-hud.tsx":{"integrity":"sha384-RKTVTqgYOCXUK/Ztei5exeXq/eeKgLJYoB6i+v9kwAJOiQsWNVJhCL7YOt9bC2kD","size":1789},"scripts/leaderboard.ts":{"integrity":"sha384-dyYY7XmhpYRYuoVvK+Gwn6iWbT7avt03iiNBuKK+5vyZPJpKBKzrv6ZiAolzayaW","size":4011},"scripts/pure/board.ts":{"integrity":"sha384-ks1PrCQkVaxlFeV4JEK7VUtIstr9rmU4dBqQLqN0QThlYOZ1StHQ5Vex+djFVxN9","size":3394},"thumbnail.png":{"integrity":"sha384-PqQ+DIWqDPRtuQIuK4NvtFcicWiCgQ94XqhBZWKGArKe+GEyHpjOdd9cerdgyamu","size":5768}}},{"name":"loveseat","data":"{\n \"id\": \"78050d8f-ee51-4db7-b060-14e7a0b48939\",\n \"name\": \"Loveseat\",\n \"description\": \"A two-seat sofa. Players can sit at either end.\",\n \"category\": \"custom\",\n \"tags\": [\n \"seat\",\n \"furniture\",\n \"sit\"\n ],\n \"version\": \"1.1.0\",\n \"changelog\": [\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The seat no longer runs on the Multiplayer Server, where it was walking every sit spot's transform chain each frame for nobody to see.\"\n },\n {\n \"version\": \"1.0.1\",\n \"notes\": \"Ships the model's texture, which was missing from the folder — without it the model rendered blank white in-world.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n },\n \"requiredPermissions\": [\n \"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE\",\n \"ALLOW_TO_TRIGGER_AVATAR_EMOTE\"\n ],\n \"group\": \"Seats\"\n}\n","files":{"Couch_01.glb":{"integrity":"sha384-Ybann6g/XRgUPIClpvLHX3e6SqETbaQ5vwLxVFADEhbMCc4YY3Jzw/RH6hR9gxSj","size":26236},"composite.json":{"integrity":"sha384-6gJtUvbDA9wZB5J/eUzFxTl2Ior4BViiorJCNokaAIZk/6/qXcg/7S1zGC+NsR6N","size":2005},"data.json":{"integrity":"sha384-uAOd4Pflpvbc2DFGnB3IX/xsDWHwz+RsG7UDDmuMluZHnB1bzIxFhw8+NEzh7T9/","size":872},"file1.png":{"integrity":"sha384-hkHg23U+9Yx8eU+7OWRgOsHJ1gS8nzdWZ/DEAQvtwJF3dOf7sTQwINVaIo30FRk5","size":7117},"scripts/seat.ts":{"integrity":"sha384-qTc33dk7kx9+avQ4JugIv1csCyESSIOVst3FKQhN3pC0OIyeh/yA32h0z8zjdIWc","size":6959},"thumbnail.png":{"integrity":"sha384-Y12yGhzQFK62sP07Qz6ipUPa++ZBer/QiCMMbpn1qnP/7n6RAJXoaDMYSGSvA4Hh","size":23593}}},{"name":"modern-sofa","data":"{\n \"id\": \"14706421-460b-4161-b06c-8ce7db73ad11\",\n \"name\": \"Modern Sofa\",\n \"description\": \"A low modern sofa, seating three. Players can sit along it.\",\n \"category\": \"custom\",\n \"tags\": [\n \"seat\",\n \"furniture\",\n \"sit\"\n ],\n \"version\": \"1.1.0\",\n \"changelog\": [\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The seat no longer runs on the Multiplayer Server, where it was walking every sit spot's transform chain each frame for nobody to see.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n },\n \"requiredPermissions\": [\n \"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE\",\n \"ALLOW_TO_TRIGGER_AVATAR_EMOTE\"\n ],\n \"group\": \"Seats\"\n}\n","files":{"ModernSofa.glb":{"integrity":"sha384-WT/qnGZedI5qRlBpLTndtxD90++WGbyQPpFeZJTWBDHR3555y88+LN4RG4aXLPA+","size":36796},"composite.json":{"integrity":"sha384-r6g/u0LMvBeUh8W3qMWZJ7H6OqgyqjNsPAL7jX5wOz+kvRwb178v0cD3YGoazd/T","size":2022},"data.json":{"integrity":"sha384-oMvACe5vrge+hMwWAsi9XHAe/OaFSCeWN8djSih9ET+M0j6CHHD5+tDxln/6crnw","size":714},"scripts/seat.ts":{"integrity":"sha384-qTc33dk7kx9+avQ4JugIv1csCyESSIOVst3FKQhN3pC0OIyeh/yA32h0z8zjdIWc","size":6959},"thumbnail.png":{"integrity":"sha384-+rS3k/5fjWqEHdGpcCs+Lur4gU1mESTaJ+fxHjpRjsKsUMbY1m6BP9G5VNwHtDea","size":16342}}},{"name":"outdoor-chair","data":"{\n \"id\": \"d8cb5c0f-dcf2-4fda-be37-2b76c4c0e78c\",\n \"name\": \"Outdoor Chair\",\n \"description\": \"A weathered outdoor chair. Players can sit on it.\",\n \"category\": \"custom\",\n \"tags\": [\n \"seat\",\n \"furniture\",\n \"sit\"\n ],\n \"version\": \"1.1.0\",\n \"changelog\": [\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The seat no longer runs on the Multiplayer Server, where it was walking every sit spot's transform chain each frame for nobody to see.\"\n },\n {\n \"version\": \"1.0.1\",\n \"notes\": \"Ships the model's texture, which was missing from the folder — without it the model rendered blank white in-world.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n },\n \"requiredPermissions\": [\n \"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE\",\n \"ALLOW_TO_TRIGGER_AVATAR_EMOTE\"\n ],\n \"group\": \"Seats\"\n}\n","files":{"Chair_07.glb":{"integrity":"sha384-rh67Oyki0AbHvwI7VAKxG4R6BvIZ+ApNvK3Ub3pjli95VKdib6IEUeCQIS5xRtIy","size":26632},"composite.json":{"integrity":"sha384-SdEMJ9948Cqop6/F49vW9182dpnKhA6sj/sGGfRC5+7NLSOvMk/wmxej4ptvjZwX","size":1419},"data.json":{"integrity":"sha384-erXzZe+COuUtzmtc67RGz02/w4ROQZMqgeU5TAsRJMcF0D1Pjj72jmzpKYJtjULa","size":879},"file1.png":{"integrity":"sha384-hkHg23U+9Yx8eU+7OWRgOsHJ1gS8nzdWZ/DEAQvtwJF3dOf7sTQwINVaIo30FRk5","size":7117},"scripts/seat.ts":{"integrity":"sha384-qTc33dk7kx9+avQ4JugIv1csCyESSIOVst3FKQhN3pC0OIyeh/yA32h0z8zjdIWc","size":6959},"thumbnail.png":{"integrity":"sha384-MuAlhtUctXmuZuYnp2/jzIowRuJoMj56xdErvUjb1WC8IJKidThtoJXtoDh1Mi2u","size":22220}}},{"name":"pickup-item","data":"{\n \"id\": \"3dd512ef-86ba-4ca2-8974-dc7629ded037\",\n \"name\": \"Pickup Item\",\n \"description\": \"Something players pick up with E (or the phone's action button), carry in the right hand, drop and throw. The Multiplayer Server decides who holds it, and the player sees it in hand at once. Works with: Work Station.\",\n \"category\": \"custom\",\n \"tags\": [\n \"pickup\",\n \"carry\",\n \"hold\",\n \"item\",\n \"ingredient\",\n \"throw\",\n \"multiplayer\"\n ],\n \"requiresSdk\": \"auth-server\",\n \"version\": \"1.0.0\",\n \"changelog\": [\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release: one holder at a time decided by the Multiplayer Server, held in the right hand and shown at once, a floating prompt with the device's own control, drop, optional throw, endless supply crates, and a script API to read, give and take what players carry.\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n }\n}\n","files":{"ai.md":{"integrity":"sha384-CzKBYLtOWD8mljnxlNig5FXr+fvWXIfxtSOHA5xWGcht/zTTS+cAO8upLSnaRqvn","size":3877},"composite.json":{"integrity":"sha384-2FkUxWGQrVSUBrsJ+VIOsWI6UM2xaB5Zwx24VZSuu9Eygn+gZHtHZs0+3Efb7hca","size":1118},"data.json":{"integrity":"sha384-SCVQG/tntqzcNSWqAx+xKsX+yM1Mq8i3uQlk7aus9g7PuLtjbOgcvmzoqfUzFTZh","size":897},"scripts/pickup-item.ts":{"integrity":"sha384-McRdI62+bnf+FbGoUaQUj8GkQq9ZQaqsMGexK30YHX9hkZbnwCd85jzgv49awInR","size":1712},"thumbnail.png":{"integrity":"sha384-2NzW1gGzZb7s0Dazwb5HwmzYU+SvssGabNn1GyXPh5tKslvz58F+6LRUfhmferWV","size":6826}}},{"name":"run-progress","data":"{\n \"id\": \"8ea22784-81dd-4d60-b0ae-54fef4852fb7\",\n \"name\": \"Run Progress\",\n \"description\": \"Timed runs with per-player checkpoints, collectibles, finish conditions, restart and a configurable HUD.\",\n \"category\": \"custom\",\n \"group\": \"Multiplayer Server\",\n \"tags\": [\n \"course\",\n \"race\",\n \"checkpoint\",\n \"collectible\",\n \"timer\",\n \"hud\",\n \"restart\"\n ],\n \"requiresSdk\": \"auth-server\",\n \"version\": \"1.4.0\",\n \"changelog\": [\n {\n \"version\": \"1.4.0\",\n \"notes\": \"The run HUD fits phones: Restart moves from the bottom-right corner (under the jump button) to under the clock, panels stack in the shared HUD layout instead of overlapping other HUDs, the controls hint matches the device, and the completion screen hides the touch controls while it is open.\"\n },\n {\n \"version\": \"1.3.1\",\n \"notes\": \"Hit points show unless Health & Respawn's Health Bar is turned off, so older Health & Respawn copies keep their bar.\"\n },\n {\n \"version\": \"1.3.0\",\n \"notes\": \"A missing spawn marker respawns players on its area's floor, not mid-air, and a checkpoint/marker count mismatch shows as a problem.\"\n },\n {\n \"version\": \"1.2.0\",\n \"notes\": \"HUD setting: Full, one big centred timer, or Hidden for a custom HUD. Health shows only when Health & Respawn turns its bar on.\"\n },\n {\n \"version\": \"1.1.0\",\n \"notes\": \"A missing spawn marker no longer stops the course: players respawn at the matching area, or at Health & Respawn's point.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Timed course runs with per-player checkpoints, collectible goals, restart, best times, a persisted leaderboard and a themed HUD.\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n }\n}\n","files":{"ai.md":{"integrity":"sha384-HEhvBW1B6ev3G9F3NJwBtkHfi3fuVS7ktxQLoQodEYlXnmA989fVh1/1U/RofXWw","size":6114},"composite.json":{"integrity":"sha384-VLC5nKAXOEJSV0e4pJv9kCp49u4nb6+OKeQImT7dYeDYMq3D21bDbiE79oyA4t0q","size":342},"data.json":{"integrity":"sha384-UrkVn6sNFnSOZ117kMO+A1RCAFy0BNX0VqJltOkoUtgeXkf6nnPPWSE3FyKUal4M","size":1751},"scripts/moving-platform.ts":{"integrity":"sha384-+OOc2vW73HOhsJCLElk2hGAcIJHfia3CKJbWkP7mOs0H7iAX3Gh5HYbcLUAZLXWh","size":1035},"scripts/run-progress.tsx":{"integrity":"sha384-qdQ+X5cdmZ4W5aogIDKuOghX0zGNiJkZE1zpq0F7iHUikOjDIX9Pe91Q4ghKjHw2","size":7645},"thumbnail.png":{"integrity":"sha384-6TEe0tNGVqIR8qvFRYv3jBgSHl7T2lZWQe6BuFofv7Lsfmwy2A6/ZHBc/Y1N6C2q","size":28248}}},{"name":"rustic-bench","data":"{\n \"id\": \"d6e264c5-a080-4702-827f-dcad4bfbc77c\",\n \"name\": \"Rustic Bench\",\n \"description\": \"A rough timber bench, seating two. Players can sit on it.\",\n \"category\": \"custom\",\n \"tags\": [\n \"seat\",\n \"furniture\",\n \"sit\"\n ],\n \"version\": \"1.1.0\",\n \"changelog\": [\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The seat no longer runs on the Multiplayer Server, where it was walking every sit spot's transform chain each frame for nobody to see.\"\n },\n {\n \"version\": \"1.0.1\",\n \"notes\": \"Ships the model's texture, which was missing from the folder — without it the model rendered blank white in-world.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n },\n \"requiredPermissions\": [\n \"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE\",\n \"ALLOW_TO_TRIGGER_AVATAR_EMOTE\"\n ],\n \"group\": \"Seats\"\n}\n","files":{"Bench_01.glb":{"integrity":"sha384-HvKGBqFZmpfflUDGmAv6bnKDgdLIRfY2gJbg+AffL+GjBuNBHpMsCpXWxfLXZDlN","size":38296},"PiratesPack_TX.png.png":{"integrity":"sha384-tvHmsChchDas/4FWyTySQozen2k/SnbQEzXKuV6p90FHRLZoH5NQyiDa9EOnNiHi","size":8600},"composite.json":{"integrity":"sha384-gGr5duM8zbC3xcX1ZPdVi5VH9qbExCukBk71DnHbpKDVp3R7BXD5Zi13eDIBoQE6","size":2007},"data.json":{"integrity":"sha384-7yCiT9oSixpR2SmV25CFxXVEFyPzmMI6/CMoXUIZp3vE1Vu0R591Vp6gjJalb/BE","size":886},"scripts/seat.ts":{"integrity":"sha384-qTc33dk7kx9+avQ4JugIv1csCyESSIOVst3FKQhN3pC0OIyeh/yA32h0z8zjdIWc","size":6959},"thumbnail.png":{"integrity":"sha384-ZmLePG5FIbmqf4RKCdidRKla6XAeP/+r51k4afbck7j8BR0RSwHEIDo7QWOSmmN2","size":11775}}},{"name":"rustic-round-stool","data":"{\n \"id\": \"80f58415-1e99-4ac3-ab21-7e4b93d8c1da\",\n \"name\": \"Rustic Round Stool\",\n \"description\": \"A round wooden stool. Players can sit on it.\",\n \"category\": \"custom\",\n \"tags\": [\n \"seat\",\n \"furniture\",\n \"sit\"\n ],\n \"version\": \"1.1.0\",\n \"changelog\": [\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The seat no longer runs on the Multiplayer Server, where it was walking every sit spot's transform chain each frame for nobody to see.\"\n },\n {\n \"version\": \"1.0.1\",\n \"notes\": \"Ships the model's texture, which was missing from the folder — without it the model rendered blank white in-world.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n },\n \"requiredPermissions\": [\n \"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE\",\n \"ALLOW_TO_TRIGGER_AVATAR_EMOTE\"\n ],\n \"group\": \"Seats\"\n}\n","files":{"Chairwood_01.glb":{"integrity":"sha384-UGMRBagdJnoipOK+RMQftg0R6vXgHD/6jTQJl45SOrw8uCclBWodtVxQj8lAwH97","size":26292},"PiratesPack_TX.png.png":{"integrity":"sha384-tvHmsChchDas/4FWyTySQozen2k/SnbQEzXKuV6p90FHRLZoH5NQyiDa9EOnNiHi","size":8600},"composite.json":{"integrity":"sha384-+vEWwi/GBNSbSKxucA5WevFwKuJvMkgdOzlA0UxB3vlTLjhfNr/db8qB9zLHoSsP","size":1423},"data.json":{"integrity":"sha384-+78WMw5CS6ay5o4yyZQC7P3dtmWW4rzjnd9j1FmyNJoAuHd4E0QeZH1bPJjJzIgZ","size":879},"scripts/seat.ts":{"integrity":"sha384-qTc33dk7kx9+avQ4JugIv1csCyESSIOVst3FKQhN3pC0OIyeh/yA32h0z8zjdIWc","size":6959},"thumbnail.png":{"integrity":"sha384-N3nz6ZelKTPqOpx5g6JLCNox5W71EwtvihYfFZffMoPDlR/oDSSVk2c44Wt/ABLH","size":13077}}},{"name":"rustic-square-stool","data":"{\n \"id\": \"9bc6d2f4-fb0a-4688-b81b-a4a83636193d\",\n \"name\": \"Rustic Square Stool\",\n \"description\": \"A square wooden stool. Players can sit on it.\",\n \"category\": \"custom\",\n \"tags\": [\n \"seat\",\n \"furniture\",\n \"sit\"\n ],\n \"version\": \"1.1.0\",\n \"changelog\": [\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The seat no longer runs on the Multiplayer Server, where it was walking every sit spot's transform chain each frame for nobody to see.\"\n },\n {\n \"version\": \"1.0.1\",\n \"notes\": \"Ships the model's texture, which was missing from the folder — without it the model rendered blank white in-world.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n },\n \"requiredPermissions\": [\n \"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE\",\n \"ALLOW_TO_TRIGGER_AVATAR_EMOTE\"\n ],\n \"group\": \"Seats\"\n}\n","files":{"Chairwood_02.glb":{"integrity":"sha384-IZcQfO7nBmiElbfMxtEy8WimKIB3WAefGB9cnz1A8oih4z6EuLuRleGbYwNr4DYZ","size":40548},"PiratesPack_TX.png.png":{"integrity":"sha384-tvHmsChchDas/4FWyTySQozen2k/SnbQEzXKuV6p90FHRLZoH5NQyiDa9EOnNiHi","size":8600},"composite.json":{"integrity":"sha384-Xqpw2Y5Vi9LBJsQf7uJIxPgt2ypqG7mIcjiQdnz2pAgbD9KKq88XHN22EW2vctRg","size":1424},"data.json":{"integrity":"sha384-pVW/W1QTcprakc0Yei7HEYqFpBmpDLHrzVANvYbipoPpWPf1rAI6rw4vFI9SUDys","size":881},"scripts/seat.ts":{"integrity":"sha384-qTc33dk7kx9+avQ4JugIv1csCyESSIOVst3FKQhN3pC0OIyeh/yA32h0z8zjdIWc","size":6959},"thumbnail.png":{"integrity":"sha384-8rf/o+kVZ7qhW/3D8hUzNanZVTfMvUMOB5+Zs85Tvn6EWobBD42Bvkpr2zuTMzbE","size":14789}}},{"name":"server-clock","data":"{\n \"id\": \"9e37c253-b33e-4505-8231-530f62715d21\",\n \"name\": \"Server Clock\",\n \"description\": \"A clock showing NTP-synced server time, identical for every player. Runs on the Multiplayer Server.\",\n \"category\": \"custom\",\n \"group\": \"Multiplayer Server\",\n \"tags\": [\n \"multiplayer\",\n \"server\",\n \"clock\",\n \"time\"\n ],\n \"requiresSdk\": \"auth-server\",\n \"version\": \"1.2.0\",\n \"changelog\": [\n {\n \"version\": \"1.2.0\",\n \"notes\": \"The 2D clock takes its place in the shared HUD layout, so it no longer overlaps other HUDs on any screen.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release: the Multiplayer Server's NTP-synced time, identical for every player, shown as 3D text at the item or as a 2D overlay on the player's client.\"\n },\n {\n \"version\": \"1.1.0\",\n \"notes\": \"Shared UI theme and independent clocks when multiple items use the overlay.\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n }\n}\n","files":{"ai.md":{"integrity":"sha384-/Awa1V4naWhY7vA66q5GEKJ4Aq7ZLOOx3xL9IKKVk5dKVMtA/MN9nWzHBA5FS5k1","size":3296},"composite.json":{"integrity":"sha384-3endi+ZKs5JfksNFuC3a+KvjsuCSyG9Ee23/mA/DKuwGPqtfLasLie3EiRd2Hqs/","size":907},"data.json":{"integrity":"sha384-6X6uh0Isy56LSYlumS2fWeQ+BL4ubUMofh/Ry7/ZF3hW2HpBdejep7t+o8bKffwt","size":955},"scripts/server-clock.ts":{"integrity":"sha384-TcnM9kQBRxlqHv6fFVkLLZ0JTEkaATZapl2FeZcCV6m0rxqtPN5VpGfzTU4vMA7W","size":2374},"thumbnail.png":{"integrity":"sha384-7vDACZ6Qb0G/LcQXW0w7DfZ/IyyT6NOmez0ZzJYgnwT9YECleSVRAHrXNZrWpVAo","size":8939}}},{"name":"simple-chair","data":"{\n \"id\": \"fe41823d-a0ef-4152-bf66-b9f44cdcffa2\",\n \"name\": \"Simple Chair\",\n \"description\": \"A simple lightweight chair. Players can sit on it.\",\n \"category\": \"custom\",\n \"tags\": [\n \"seat\",\n \"furniture\",\n \"sit\"\n ],\n \"version\": \"1.1.0\",\n \"changelog\": [\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The seat no longer runs on the Multiplayer Server, where it was walking every sit spot's transform chain each frame for nobody to see.\"\n },\n {\n \"version\": \"1.0.1\",\n \"notes\": \"Ships the model's texture, which was missing from the folder — without it the model rendered blank white in-world.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n },\n \"requiredPermissions\": [\n \"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE\",\n \"ALLOW_TO_TRIGGER_AVATAR_EMOTE\"\n ],\n \"group\": \"Seats\"\n}\n","files":{"Chair_03.glb":{"integrity":"sha384-Kr1BoWrtvWuVgiTcU0TmlB2hNVHJIOjoEXu27uAvSWezai6Nn+MYq9IkPAPTVjZy","size":10140},"composite.json":{"integrity":"sha384-Ah3wQrl5kn40r1Byv3A0InbOVhRgn9Z3559lyvQCCnFhRdPjR88/pngxS0lOWYlQ","size":1444},"data.json":{"integrity":"sha384-QcAWIr/jmyykog8qMz+2TmM7WCiqgbH6WpQA7iPbyx0cvKJRIvu/FpCOwFoeCy/P","size":879},"file1.png":{"integrity":"sha384-hkHg23U+9Yx8eU+7OWRgOsHJ1gS8nzdWZ/DEAQvtwJF3dOf7sTQwINVaIo30FRk5","size":7117},"scripts/seat.ts":{"integrity":"sha384-qTc33dk7kx9+avQ4JugIv1csCyESSIOVst3FKQhN3pC0OIyeh/yA32h0z8zjdIWc","size":6959},"thumbnail.png":{"integrity":"sha384-970of2uoK/Vy8uBQAQRDTI5ubtyaXF6NVB+QdweA/VzCaUyVlHTciY2bgnX4bbcs","size":10304}}},{"name":"sit-spot","data":"{\n \"id\": \"49bc2e26-dc56-430b-888e-509d56ae79ed\",\n \"name\": \"Sit Spot\",\n \"description\": \"An invisible seat: drop it on any surface and players can sit exactly there, facing the way it points.\",\n \"category\": \"custom\",\n \"tags\": [\n \"seat\",\n \"furniture\",\n \"sit\"\n ],\n \"version\": \"1.1.0\",\n \"changelog\": [\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The seat no longer runs on the Multiplayer Server, where it was walking every sit spot's transform chain each frame for nobody to see.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n },\n \"requiredPermissions\": [\n \"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE\",\n \"ALLOW_TO_TRIGGER_AVATAR_EMOTE\"\n ],\n \"group\": \"Seats\"\n}\n","files":{"composite.json":{"integrity":"sha384-uhBFFGia5mFzOQWC/J9UxtsSaldLqPEHiwzfnBYPzokXoME91oNDnBhwjPMNjpNc","size":799},"data.json":{"integrity":"sha384-ZpWPifWI1oc5/I6vlAeNNosb8cmuu5IEKnh/rdh8vdf57V+wnNsuVqAuFYn5aVdG","size":754},"scripts/seat.ts":{"integrity":"sha384-qTc33dk7kx9+avQ4JugIv1csCyESSIOVst3FKQhN3pC0OIyeh/yA32h0z8zjdIWc","size":6959},"thumbnail.png":{"integrity":"sha384-OrhbCwB59TLqDClVqt8Hrcy+OPXXkctZ1vW7zQTWOSaEWnyNRyne5SBtsVrZKVt2","size":18923}}},{"name":"sit-spot-edge","data":"{\n \"id\": \"6c815b58-d13f-4d30-a7ea-0f275d740084\",\n \"name\": \"Edge Sit Spot\",\n \"description\": \"An invisible seat for ledges and edges — the player sits with their legs hanging over the side.\",\n \"category\": \"custom\",\n \"tags\": [\n \"seat\",\n \"furniture\",\n \"sit\",\n \"edge\",\n \"ledge\"\n ],\n \"version\": \"1.1.0\",\n \"changelog\": [\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The seat no longer runs on the Multiplayer Server, where it was walking every sit spot's transform chain each frame for nobody to see.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n },\n \"requiredPermissions\": [\n \"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE\",\n \"ALLOW_TO_TRIGGER_AVATAR_EMOTE\"\n ],\n \"group\": \"Seats\"\n}\n","files":{"composite.json":{"integrity":"sha384-us527Hlk6/JM3bpEqqwHclf7+Xu8hXGrtrhZJ1w/atuO4025ZqJO80d0Ov4XIXnq","size":764},"data.json":{"integrity":"sha384-CH6sS8qFyE2hi/eP5h6IP+XIhtaTkrANQsfRDKzipE1+a3/anhHvfoXa66xmW6g3","size":779},"emotes/Sit_Edge1_emote.glb":{"integrity":"sha384-8oYD8S4HilS8mFsgOj5lRbBWoq3rmF7ETStGkvefUODtwN+lo5gxdKY/F80aqLIj","size":99400},"emotes/Sit_Edge2_emote.glb":{"integrity":"sha384-bS47w9rNkTXOUXmHoSOTkM8g+3AcRbSRLwGblftdTa2Hr0avdubdOdiC2D5bkM2E","size":89300},"scripts/seat.ts":{"integrity":"sha384-XonZTJcIqIC//h6SCTzpxOTs+MfpK2IHjkDTg1t+XmUUSZ7777snXkZeRzpahO7Q","size":7017},"thumbnail.png":{"integrity":"sha384-OrhbCwB59TLqDClVqt8Hrcy+OPXXkctZ1vW7zQTWOSaEWnyNRyne5SBtsVrZKVt2","size":18923}}},{"name":"small-couch","data":"{\n \"id\": \"acca7441-52c9-4b80-83fb-af18bcbf4c7e\",\n \"name\": \"Small Couch\",\n \"description\": \"A compact couch, seating two. Players can sit at either end.\",\n \"category\": \"custom\",\n \"tags\": [\n \"seat\",\n \"furniture\",\n \"sit\"\n ],\n \"version\": \"1.1.0\",\n \"changelog\": [\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The seat no longer runs on the Multiplayer Server, where it was walking every sit spot's transform chain each frame for nobody to see.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n },\n \"requiredPermissions\": [\n \"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE\",\n \"ALLOW_TO_TRIGGER_AVATAR_EMOTE\"\n ],\n \"group\": \"Seats\"\n}\n","files":{"TwoSeater_Couch.glb":{"integrity":"sha384-3aFVc1VRYCZMbuV54qKD37CG/Gyy8g4KkdPSjqqbmNMVELB8RVdCVn4bhaaJeTIA","size":35116},"composite.json":{"integrity":"sha384-4SsVtae9U3jtnd8ws0MFI6eEqXQA+K9d0YanHazJzExwBdh4yL/10B6oDCa5m9OE","size":2006},"data.json":{"integrity":"sha384-u6WCss6xFK0tfHpKnL40yNXkaJg1VNGwvL6RdTYzaSWbFZGSOUHJh0fK6CZhN4Io","size":715},"scripts/seat.ts":{"integrity":"sha384-qTc33dk7kx9+avQ4JugIv1csCyESSIOVst3FKQhN3pC0OIyeh/yA32h0z8zjdIWc","size":6959},"thumbnail.png":{"integrity":"sha384-Vo9jNKeVjbITSNCMK5+1FvD6QgI6prs0AZptiMN1LybCqlnhS3syD/yd7o6Dhos9","size":15304}}},{"name":"spawner","data":"{\n \"id\": \"fc759fc0-57a1-461c-aa06-22c0d3d4c3e0\",\n \"name\": \"Spawner\",\n \"description\": \"Makes copies of a prefab appear while playing — on a click, when a player walks in, on a timer, or when another script asks. Copies appear at the Spawner's own spot, only on this player's client.\",\n \"category\": \"custom\",\n \"tags\": [\n \"spawner\",\n \"spawn\",\n \"pickup\",\n \"enemy\"\n ],\n \"version\": \"1.0.0\",\n \"changelog\": [\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release: copies of a prefab appear while playing — on a click, when a player walks in, on a timer, or when another Script asks — made on that player's own client, with a per-spot cap, a lifetime and scatter.\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n }\n}\n","files":{"ai.md":{"integrity":"sha384-z0Sma23V3knVRWNDZMCDl+EKMeObpA2NxJzO543HDEWXdjM8rq2BUvW8ivVC3s2r","size":5423},"composite.json":{"integrity":"sha384-11bTytc5FvYc2S+noZUzFBRPTTCZ+JK3gMOGtS1vg57/Aqv0jIG+H6StFjCHbr+o","size":1383},"data.json":{"integrity":"sha384-te+nomjlQGpKcP1TUxjeJBvcNMY2e/q67l6VHqU0B81p9tCWgjz8qTps7gUZ2kdf","size":747},"scripts/spawner.ts":{"integrity":"sha384-2BCX+ZtBU07fyYb1gxapwzivM8DRe8i4CetRZgpVN8N5ta0vlxxFEDdfHUmEYOkJ","size":16034},"thumbnail.png":{"integrity":"sha384-4nU0PNVHtK4o6zj4kZ7zbNlQzczBQKUQPC3F8ZPrcP7BWYbExlEs3VlFajqKPwZG","size":6786}}},{"name":"steampunk-bench","data":"{\n \"id\": \"8a80b964-1f4d-4ea3-8f6b-078809891a17\",\n \"name\": \"Steampunk Bench\",\n \"description\": \"A riveted steampunk bench, seating two. Players can sit on it.\",\n \"category\": \"custom\",\n \"tags\": [\n \"seat\",\n \"furniture\",\n \"sit\"\n ],\n \"version\": \"1.1.0\",\n \"changelog\": [\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The seat no longer runs on the Multiplayer Server, where it was walking every sit spot's transform chain each frame for nobody to see.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n },\n \"requiredPermissions\": [\n \"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE\",\n \"ALLOW_TO_TRIGGER_AVATAR_EMOTE\"\n ],\n \"group\": \"Seats\"\n}\n","files":{"Steampunk Bench.glb":{"integrity":"sha384-l8rvgt9Z8JKpRwRI3rxw9qoH/vx1v7HvwxJoQWJi/xQ2v3+HIFzUE3SEMakC13lj","size":159244},"composite.json":{"integrity":"sha384-qT/zOtPEwpaAhxx6YNaSnoFHWNCzk9cluTzkbWVOT1ASokLY9hfUd+lhKDF6w/np","size":2019},"data.json":{"integrity":"sha384-ywBxOVAXX1Eo07MEC1qn5iwpz4Wxa0Sf9JHs8cYCOa0JL8D5/M0pRS/k2RuNCzD0","size":721},"scripts/seat.ts":{"integrity":"sha384-qTc33dk7kx9+avQ4JugIv1csCyESSIOVst3FKQhN3pC0OIyeh/yA32h0z8zjdIWc","size":6959},"thumbnail.png":{"integrity":"sha384-NNFHgxb81gJ0y4Z1APmww5ZHZz4My+jv53ogLyEKS+qjno5TAMnoSE4PFSELuGsR","size":16622}}},{"name":"steampunk-chair","data":"{\n \"id\": \"0cdcf518-9bb8-454e-9b18-a851b2a0b1df\",\n \"name\": \"Steampunk Chair\",\n \"description\": \"A brass-and-leather steampunk chair. Players can sit on it.\",\n \"category\": \"custom\",\n \"tags\": [\n \"seat\",\n \"furniture\",\n \"sit\"\n ],\n \"version\": \"1.1.0\",\n \"changelog\": [\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The seat no longer runs on the Multiplayer Server, where it was walking every sit spot's transform chain each frame for nobody to see.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n },\n \"requiredPermissions\": [\n \"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE\",\n \"ALLOW_TO_TRIGGER_AVATAR_EMOTE\"\n ],\n \"group\": \"Seats\"\n}\n","files":{"Steampunk Chair.glb":{"integrity":"sha384-jx1WnrmD7GigwvCDb4X6HDhs3Q4rc7J10DlakX2I31xP/64hO8E76U6nCARYu1D/","size":137292},"composite.json":{"integrity":"sha384-C9XF/cSfbX7UfnePwSOg2BrpL9m3nB5WyQRJkINrSOsmV+JoEcEAEv/2bl7pl9Kd","size":1420},"data.json":{"integrity":"sha384-qoBLKLZCSylATy78bslEs1GaUl+YjZPvDh4GBtS4cAkM5JzvXbQMX2QxWjOapOAU","size":718},"scripts/seat.ts":{"integrity":"sha384-qTc33dk7kx9+avQ4JugIv1csCyESSIOVst3FKQhN3pC0OIyeh/yA32h0z8zjdIWc","size":6959},"thumbnail.png":{"integrity":"sha384-tAJBIbkGkYJOtHKoWzPwIUmcjypRoYnqLu5S7mLiuQ5KUiHKZ493plJyalIZ5aYH","size":22358}}},{"name":"tall-stool","data":"{\n \"id\": \"f83a91fb-b939-4251-a298-d334c3e48996\",\n \"name\": \"Tall Stool\",\n \"description\": \"A tall stool with a footrest. Players can sit on it.\",\n \"category\": \"custom\",\n \"tags\": [\n \"seat\",\n \"furniture\",\n \"sit\"\n ],\n \"version\": \"1.1.0\",\n \"changelog\": [\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The seat no longer runs on the Multiplayer Server, where it was walking every sit spot's transform chain each frame for nobody to see.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n },\n \"requiredPermissions\": [\n \"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE\",\n \"ALLOW_TO_TRIGGER_AVATAR_EMOTE\"\n ],\n \"group\": \"Seats\"\n}\n","files":{"Stool_D.glb":{"integrity":"sha384-H8ztvhYutHrOV6dbXUfUSODKyMLIy/P5eEntuQxEe2zEHwMLnlQVfvqFRhWMtA+h","size":320312},"composite.json":{"integrity":"sha384-RnEdGr2iP41goFt9Gmfi5bYDprFCHPslIt+zDWXP25ohLUd8p2RO8Q6Q3fEm91w5","size":1412},"data.json":{"integrity":"sha384-C8mP3L8l99WT0GJixLoSzlekZHxR+orHexOZdET85GqVMSpbbRLCqQ6KRn9r8mjR","size":706},"scripts/seat.ts":{"integrity":"sha384-qTc33dk7kx9+avQ4JugIv1csCyESSIOVst3FKQhN3pC0OIyeh/yA32h0z8zjdIWc","size":6959},"thumbnail.png":{"integrity":"sha384-/VHtkn6ezh/nPA9bGkGF7eNlD7G/e+32phC0qmZ0/LSbEtn+jo5mWb+1RfzomHQW","size":11715}}},{"name":"trigger-zone","data":"{\n \"id\": \"f1794ec8-ed62-42c8-a71b-6c52e04b161a\",\n \"name\": \"Trigger Area\",\n \"slug\": \"trigger_zone\",\n \"description\": \"An invisible area that knows who is standing in it. Name it, scale it with the gizmo, then have a script react to that name.\",\n \"category\": \"custom\",\n \"tags\": [\n \"trigger\",\n \"area\",\n \"interactivity\"\n ],\n \"drivenBy\": {\n \"rule\": \"The area only publishes who is standing in it — a Script reacts to its name through './runtime/zoneBus', on this item or on any other entity in the scene.\",\n \"code\": \"onZone('Front Door', 'enter', (event) => { /* what happens */ })\",\n \"next\": \"Use the Add a reaction button below and the new Script arrives already listening to this area.\"\n },\n \"version\": \"1.1.1\",\n \"changelog\": [\n { \"version\": \"1.1.1\", \"notes\": \"Guide covers model bounds, active parcels and course prefab integration.\" },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release: an invisible named area that publishes who is standing in it, so a Script anywhere in the scene reacts to it by name with nothing wired.\"\n },\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The placed area's Script card now shows the onZone line a Script reacts with, under its area settings.\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n }\n}\n","files":{"ai.md":{"integrity":"sha384-D++yhIulqWPCrKizIyGTqDJF2ZxBv5GUgm9n8tbZKFO2xiwWUs8H5jMl49jSr+GK","size":6096},"composite.json":{"integrity":"sha384-VWkNF79zDduwdioBw5OubcoLdmnXzzZq6GXkq3u3k5qwD4JDktJw+wiLhI3Hzto+","size":917},"data.json":{"integrity":"sha384-r3pReo7kADyb6lmgav6cxgC2hwAVQoEGbLaINBkvAgkObtAk517Tf/GGZPI9Ocov","size":1286},"scripts/trigger-zone.ts":{"integrity":"sha384-57lvbBOHUXnIGndfQD1G9oRA9gVOpLg7JWwSAV9LJzeD6xGyTKCPhuLai3nhXE9R","size":3837},"thumbnail.png":{"integrity":"sha384-4htEAGokLp2yZKaMa5PZanI+3zqPe3TnLcXpmfyJjfnXhYmij+KVgZ51pXdeTnXN","size":4283}}},{"name":"ui-theme","data":"{\n \"id\": \"41939776-72b2-45f1-b40a-097fa66c9790\",\n \"name\": \"UI Theme\",\n \"description\": \"One accent color and appearance for game HUDs, timers, leaderboards, buttons, results and announcements.\",\n \"category\": \"custom\",\n \"group\": \"Scene UI\",\n \"tags\": [\n \"ui\",\n \"theme\",\n \"color\",\n \"hud\",\n \"design\"\n ],\n \"version\": \"1.1.0\",\n \"changelog\": [\n {\n \"version\": \"1.1.0\",\n \"notes\": \"Custom HUDs are placed through the shared HUD layout, which keeps them clear of the phone touch controls and of each other.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"One accent color and dark or light appearance shared by the scene's game HUDs, buttons, timers, leaderboards and results.\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n }\n}\n","files":{"ai.md":{"integrity":"sha384-nxOv4VlTrgwQXS1doZ8l2Ka0Lf+49wlK5fZe6IeN6ym0d49eVorEF0pkP+mW0zup","size":2263},"composite.json":{"integrity":"sha384-GLLTfiifHN/3BNFuV3tozHl5PqIr2Sw/1lsM/jQQ8BcUY8FIt6gPCUgxcloJaxsW","size":551},"data.json":{"integrity":"sha384-gPrFuwJDKwYj8TGgJIkhTtOc+M6nawLruchbna5wTiCJ+3yXQQ3HlLyc/Ck0xpB8","size":766},"scripts/ui-theme.ts":{"integrity":"sha384-InUblgjcHMPeDZ/IKir/gTVfws6YE4FkR/SUd0AA285wV7rJUfEorIxCetjIiXNj","size":420},"thumbnail.png":{"integrity":"sha384-Q+XsR3csSrtPv1vWF2KsovKOoh15mPBgVsBKlMdNnjJSOJi4JhhkrPZEBpjdzjRR","size":5983}}},{"name":"video-screen","data":"{\n \"id\": \"37188734-4562-4f21-afeb-2752c39278f4\",\n \"name\": \"Video Screen\",\n \"description\": \"A flat screen that plays a video file or a live stream, with volume and playback controls.\",\n \"category\": \"custom\",\n \"tags\": [\n \"video\",\n \"screen\",\n \"stream\"\n ],\n \"version\": \"1.0.0\",\n \"changelog\": [\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n }\n}\n","files":{"composite.json":{"integrity":"sha384-PNwL0kv1USZnEFOSnC3oF6kLaG3Hy0S5VGGvk16R+dkdkIidEPn40/SpRI4Eipsm","size":2536},"data.json":{"integrity":"sha384-CWXT5tqC3clzqs6wTwRMXrxBBT8PdHaNRpa4H4SFhZBVw4GfOe9/vmdjzk58KTaK","size":427},"thumbnail.png":{"integrity":"sha384-OQnhEX0GsIogArcrXaPYIo5bRqaYsK7V6GnniDUiiHDPOmojTFQKbcopVX8poMbd","size":3919},"video_player.glb":{"integrity":"sha384-Su82WkaQqG6obG/yGRkHw+WCXQCX/vMngfKyCV/IUH7tpdqz1MS2777hwjPMNdrI","size":1276}}},{"name":"wooden-chair","data":"{\n \"id\": \"c0f3a3f1-7fad-47bf-a855-e27f41f7894f\",\n \"name\": \"Wooden Chair\",\n \"description\": \"A plain wooden chair. Players can sit on it.\",\n \"category\": \"custom\",\n \"tags\": [\n \"seat\",\n \"furniture\",\n \"sit\"\n ],\n \"version\": \"1.1.0\",\n \"changelog\": [\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The seat no longer runs on the Multiplayer Server, where it was walking every sit spot's transform chain each frame for nobody to see.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n },\n \"requiredPermissions\": [\n \"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE\",\n \"ALLOW_TO_TRIGGER_AVATAR_EMOTE\"\n ],\n \"group\": \"Seats\"\n}\n","files":{"Chair 1.glb":{"integrity":"sha384-OBKh/Bj43q8Sx1wRponwlbgpevgaWMUiN0giUov/12Oi6zrbOTERiktleBfycvzW","size":34444},"composite.json":{"integrity":"sha384-Bl50OjO++oWtYNycDeONS6QDpxb6NUTuEcNYezYybT6QL2mKs1e+Ar7bZi9C8vQ7","size":1411},"data.json":{"integrity":"sha384-5LcT4SUEiXzUl7Fm686bC0qwplC+5wqCoijTQxWYqrKqiayYS6IBeCF8cfvayJS9","size":700},"scripts/seat.ts":{"integrity":"sha384-qTc33dk7kx9+avQ4JugIv1csCyESSIOVst3FKQhN3pC0OIyeh/yA32h0z8zjdIWc","size":6959},"thumbnail.png":{"integrity":"sha384-85oGX4HxIuc6wLmDYZYFS2Et0rxNcuex0sgvRhWn/hG3xXfMjbhWHOq16rkU629x","size":18385}}},{"name":"wooden-chair-armrests","data":"{\n \"id\": \"ebd62f4c-fa46-489f-bf8e-104aafc10b0f\",\n \"name\": \"Wooden Chair Armrests\",\n \"description\": \"A wooden chair with armrests. Players can sit on it.\",\n \"category\": \"custom\",\n \"tags\": [\n \"seat\",\n \"furniture\",\n \"sit\"\n ],\n \"version\": \"1.1.0\",\n \"changelog\": [\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The seat no longer runs on the Multiplayer Server, where it was walking every sit spot's transform chain each frame for nobody to see.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n },\n \"requiredPermissions\": [\n \"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE\",\n \"ALLOW_TO_TRIGGER_AVATAR_EMOTE\"\n ],\n \"group\": \"Seats\"\n}\n","files":{"Chair 3.glb":{"integrity":"sha384-qC94v3lBqcsvDYKbxOFSBG9sXvO2WlwJ4CZlUVUKmTvKvsn94KetxdVRfO/8VJ9Z","size":40316},"composite.json":{"integrity":"sha384-8c+9B2WZVYU90TjDED4IskWU7oRBdYvk29okBEMaheh/V6vRP82wbUlM2syOB48g","size":1426},"data.json":{"integrity":"sha384-olI3fBaKG8LfISwoMdmldQE4DxzqD2THTl9YwQlF85f4L7M+FU/6uPTx1i/vs+C0","size":717},"scripts/seat.ts":{"integrity":"sha384-qTc33dk7kx9+avQ4JugIv1csCyESSIOVst3FKQhN3pC0OIyeh/yA32h0z8zjdIWc","size":6959},"thumbnail.png":{"integrity":"sha384-g1sgPvNl8JeOjulBezQcWzMoT1bkFgk/dNp9BfPv1SEQWa8dABvHvh4IDDnrKnlG","size":16240}}},{"name":"work-station","data":"{\n \"id\": \"500b804b-8d35-420a-b323-243df2e0eaa9\",\n \"name\": \"Work Station\",\n \"description\": \"A spot where a carried item is put down and taken back, and optionally worked on: hold F (or the phone's second button) to chop or wash it, or let it cook by itself, with a progress bar over it. Works with: Pickup Item.\",\n \"category\": \"custom\",\n \"tags\": [\n \"station\",\n \"counter\",\n \"craft\",\n \"cook\",\n \"chop\",\n \"progress\",\n \"multiplayer\"\n ],\n \"requiresSdk\": \"auth-server\",\n \"version\": \"1.0.0\",\n \"changelog\": [\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release: put down and take back one carried item, hold-to-work and timed modes that turn one kind into another with a progress bar, combining two kinds, and station events a script reacts to by the station's Name.\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n }\n}\n","files":{"ai.md":{"integrity":"sha384-OZWeKCetESx9LCTjPp7QnMkgK3Gw+aBxPvTIylaKnIQwkpyUsWaLrlYtA+ZXDM4c","size":3973},"composite.json":{"integrity":"sha384-o11OXBbs8puE3EBpzZP8RH2je6ApNF5Y8cbiVBvEosI/iFR/wME+hFImnGHPaHBa","size":1360},"data.json":{"integrity":"sha384-ETmIMiOAQcsf8Mya8OL3yPXEy6zFfBtDTK8+PKrA5YSnLitxVE2LgCdLs6r/m9dt","size":855},"scripts/work-station.ts":{"integrity":"sha384-xM18mqGzqGNv7xJ93IlP+B2/vSiQZjSrT+g2liniGgB0NY5cVHSaATVRdJqVt9S5","size":1798},"thumbnail.png":{"integrity":"sha384-WdzqTBASLJKRMCT5llVoVkuIrSoQL3cyutkNphHorfgVsTVoH9lY9rqlIJubWfkJ","size":6483}}}]
1
+ [{"name":"admin-tools","data":"{\n \"id\": \"14f55d0a-1691-439c-9017-8903afcce3e3\",\n \"name\": \"Admin Tools\",\n \"description\": \"In-world admin panel: moderate players, post announcements and control video screens. Only admins you list can open it.\",\n \"category\": \"custom\",\n \"tags\": [\n \"admin\",\n \"moderation\",\n \"video\",\n \"announcements\"\n ],\n \"version\": \"1.1.0\",\n \"changelog\": [\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The panel stays inside the area the explorer leaves free of its own controls, and the full-screen layer no longer blocks clicks.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release: an in-world panel only the admins you list can open — moderate players, post announcements and control the scene's video screens.\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n },\n \"requiredPermissions\": [\n \"USE_FETCH\",\n \"USE_WEB3_API\",\n \"USE_WEBSOCKET\",\n \"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE\"\n ]\n}\n","files":{"composite.json":{"integrity":"sha384-WYmVW/WiIQ9MIezdTLx0l9KALl8G6IYSnZplhupYGyuk9QOW7GXVM1GBJanG96OX","size":1886},"data.json":{"integrity":"sha384-OTrnY78c8LfxaEXN6NeJIfNscBPi6142qRQp9P9m/+GbCvKw86x/J0faFwW7ERTn","size":927},"icons/admin-panel-control-button.png":{"integrity":"sha384-gs8sS7NlWDosiswd38dlTSRMIxSmPy4uuMH9/Z0G8gWpvKerKgcp3ui7Nme/wbci","size":2217},"icons/admin-panel-moderation-control-button.png":{"integrity":"sha384-mBy3ESO3Mq875LIiSGzFVnD9iLmf9DWMqjEBRnZnT/vmQTy4cendMAM0Pw3iw4I5","size":1631},"icons/admin-panel-rewards-control-button.png":{"integrity":"sha384-lbA1p3+WUiCqMyLQZCtb/GY7G+XGMXvpmsvv6DJWVIV2F4K5G47BJGdJDcuOk9pj","size":1796},"icons/admin-panel-smart-item-control-button.png":{"integrity":"sha384-4dx0rzzVKfDcfpUwMvngJ9THOAclyEDKbUwtTo826cRQOvFez6s78DipQSJt9WMY","size":846},"icons/admin-panel-text-announcement-control-button.png":{"integrity":"sha384-Tp9W3GXQPvd9OwANHFYrfekQpNHdFzcAdRgK0KedviMH4GtXirlh1xNDZivpN7Ii","size":522},"icons/admin-panel-video-control-button.png":{"integrity":"sha384-GR1IO0b/6EbWm9nZwfSm0c6JOfNgxGMC9JmF4+dcI2E47tz5ayJ75fKIn5EdIiZn","size":726},"icons/admin-tool-background.png":{"integrity":"sha384-I87kdzFsmxJLWQNdkQAJK7ech9hyy/I5eA/LbF8+O8R+mQF/+PHTOjGoEgqnDD+M","size":792},"icons/announcements/text-announcement-chat-message.png":{"integrity":"sha384-Wamzz2shgywHVGOsvtruJrW6pEJ8BA4wLopI9m6Aj3QfLpS6+JIhVd/gQW1BeHQz","size":14307},"icons/announcements/text-announcement-check.png":{"integrity":"sha384-ocqK42WSlHJF+8PKK8yUlxlgLaq98LHnmrksUDGjZY/griiuV0QTW4LjC9N5ibj7","size":283},"icons/announcements/text-announcement-close-button.png":{"integrity":"sha384-2/dJo7RNlVSyWFmyM8PI7k2Mn7NbpTrhugMCn4b6+doaqJa83i6V1q0Lft19vMj6","size":1148},"icons/moderation-control-icon.png":{"integrity":"sha384-7A2sHB+ce6HTmfiULIsEQp/N+UZdf3tivJui0rNjghcCka7n7eSfWgl4fjvz4Cgn","size":1312},"icons/moderation/admin-panel-verified-user.png":{"integrity":"sha384-vNcq6YpNNQ7lFotU8SaOCKBhil8PLzDWsblH357op7HxAA2/M6Rg0Vj/HQzpnbZI","size":1213},"icons/moderation/ban.png":{"integrity":"sha384-cmN14nYErcvzkNCovoKGXocGHo7KGrn6R+k91cdCG/wIXtvr3RNim70WTj7rNBeN","size":470},"icons/moderation/chevron-back.png":{"integrity":"sha384-mqnl54KA63yVGjEnCmpO4OP5d0P1Yn6mKr+vBskc0k6X195fA2sby0tLRf1U/rh+","size":3165},"icons/moderation/chevron-forward.png":{"integrity":"sha384-rLe+qxxpQqYMMynmmpvQtFf7721X2H/dqLeqpe3O/PQ3GoUBU9gPU2+VrLoXbFxm","size":3171},"icons/moderation/close.png":{"integrity":"sha384-3MgIzHRKOzSovLyjR7BqmW+CDECeCtQP+qgwJYp7DyG6UncrCp69WwDmQFrAPAab","size":3851},"icons/moderation/error.png":{"integrity":"sha384-UaTdOECxl6EaoYfijFAVBut8YPdUyMQLeRibiNc1Zgl7icEN2xEXrar8IGahlkb8","size":798},"icons/moderation/person-outline.png":{"integrity":"sha384-SpgAMD/+hqIDVz9fx+47lsGoP5R04k76Ld7Vsjt/avZBqVRhfq9+1i/2cXKLYgm+","size":1085},"icons/rewards-control.png":{"integrity":"sha384-o16Vo2/34yoPpO5eaLkz2PsbfOsN6d0OKKCAiYai4QfHN3cSi6WAUJ2BNDsYcrYu","size":785},"icons/rewards/close.png":{"integrity":"sha384-3MgIzHRKOzSovLyjR7BqmW+CDECeCtQP+qgwJYp7DyG6UncrCp69WwDmQFrAPAab","size":3851},"icons/rewards/send.png":{"integrity":"sha384-S33P3X184uAhwTQAeuj9xsMUNXbZ+u/NQTEfFGBA7T0U+AJgGEf7fF++U43aoT1p","size":500},"icons/smart-item-control.png":{"integrity":"sha384-ifsViL2s+VFP4zoJYSRnK3YbSC/CHaJip0TZ+t21bWNVl70IbI1HKlCfdeW2UDSg","size":496},"icons/text-announcement-control.png":{"integrity":"sha384-7URWvpEp3gmP6yJ2hO0TRwMoGwneSaTftS0G8oJJkxEDfLuAFC0v3e5svRbe06s3","size":454},"icons/video-control.png":{"integrity":"sha384-08i8/ot11Lq7VQmc0m2GEqXsE7SydKZBg3zgTAlFEWUFw17F9+agTncc8jPekmSl","size":643},"icons/video/chevron-down.png":{"integrity":"sha384-OcKDCPWWVFuK9NILpIRUhFTh39zgaew6mFlyV7T40mJrBJXr2Q1p4/xABVEplRnf","size":343},"icons/video/chevron-up.png":{"integrity":"sha384-yX6sXRFGFknZtfhtuXNUJr3lc0DgnN8K3y2gPoHbowu9bbkdme0TxDU4/UQh9hfQ","size":346},"icons/video/copy-to-clipboard.png":{"integrity":"sha384-eyjGkM9EtomLolYvPV/xK39hFFazZIwtzd/XF34srRydJTSp2bxS3UafHk+/cP7D","size":304},"icons/video/error.png":{"integrity":"sha384-UaTdOECxl6EaoYfijFAVBut8YPdUyMQLeRibiNc1Zgl7icEN2xEXrar8IGahlkb8","size":798},"icons/video/eye-off.png":{"integrity":"sha384-xQXGDg/WIuy/UdZlYzBuwUY/d1Qp0wfSiavWmAbe5Jzs2+x2O2+qzc+wQyvpY5eS","size":9468},"icons/video/eye.png":{"integrity":"sha384-wwXqHg8tIOK/X1H/xf+6d3GWRtObvrgE553mA8H0RX05t+LFegaRQ+oekZNhfzUb","size":9696},"icons/video/help.png":{"integrity":"sha384-wur+rnzxUR5aoo/HZGtbcRtH46TTEe51XrORkzXpI6GJEzgxe26v1wB4dqUMl6rY","size":6353},"icons/video/info.png":{"integrity":"sha384-2VAiLc08J0wHLo6rbMq0dryIHmXyIwxmz+QLGvOy4CA/FoVwUw7QcxKkJdWy389l","size":37460},"icons/video/person-outline.png":{"integrity":"sha384-SpgAMD/+hqIDVz9fx+47lsGoP5R04k76Ld7Vsjt/avZBqVRhfq9+1i/2cXKLYgm+","size":1085},"icons/video/star.png":{"integrity":"sha384-9GFGGyegeJD8W7mQETsKMaajbcVwTftNidPsCZ4ZdguEqe4e7ApgMaWbbQK3Wq5q","size":612},"icons/video/video-control-dcl-cast.png":{"integrity":"sha384-jfetm7VqkoaYFM60cRDbHYFUKV9PFYdoSJoJJjnlmUMcHXJaXMdqf8CEsfTbfiW0","size":3958},"icons/video/video-control-live.png":{"integrity":"sha384-zKG2r2mRC2+KaBSuCOr8GXCooPlhdS0DM/bhkKuJcZESHLGHUmIKqKCSpBQrTtAB","size":12998},"icons/video/video-control-loop.png":{"integrity":"sha384-PmW7BM5QYFSdD7yWHRAM2ENhpXqabDVBMBraqbQZQX+FSgtzQkM+5sxOvzdnploj","size":8685},"icons/video/video-control-mute.png":{"integrity":"sha384-UNYvZ+/8Gxof8muaqCDw/PIz7H3tLC3j5WyD3vBQvf5JBnQEd0QCIlr+T24HlYPW","size":5752},"icons/video/video-control-pause-button.png":{"integrity":"sha384-cvfT4+N9agDRnU6Ow9wT+Ronr/RJx1c2CAaAt4t3xHPQLvWOJNnPlfXW/O0hyYus","size":5267},"icons/video/video-control-play-button.png":{"integrity":"sha384-CAvkQGDWHS1tYlEA/z33gKf9mvIXPAsISRbl3pdzT52NN6eF/woOQJ+ihnflI7pd","size":402},"icons/video/video-control-video-icon.png":{"integrity":"sha384-yKtdqQ/8ARbNu6EXx2DO479v9R89Qf/gkYmwT9Srj6Olr2qZom3S/TAhJSmR3Ufu","size":667},"icons/video/video-control-volume-minus-button.png":{"integrity":"sha384-P2EM6fgmDXOFDFdfWKYbfUS3/j1pIXgYe6SYkvxhOkLcVNkvDc5rM/P60COE64QP","size":188},"icons/video/video-control-volume-plus-button.png":{"integrity":"sha384-lBnQMGgUEbRTddesn4aY7SxDQ07mz3UKlB9hcwwbfJ1sO215UNs1cXIuIeGfJ4we","size":267},"scripts/actions.ts":{"integrity":"sha384-hE/4GuMatbDddD196YnJeLlpA57vKLHYTS5eRqrChRbpKSWYlfD+d69W4AWD7K52","size":7017},"scripts/admin.tsx":{"integrity":"sha384-Ej24LfV0mpU0RNLnAlt1nWWrDhrbpVFQQUu6gnPt4EO1k0IlvKz3cAfdA6vHzHfM","size":7868},"scripts/api.ts":{"integrity":"sha384-dhPp6JRdztKLmBTidsgYNe69Y/ZEFwDc73blrCgLgRtkgq6kByvEusNYrpKR8gCf","size":4464},"scripts/components.ts":{"integrity":"sha384-OvrIEnNGEwWpatTyqapsXXniSDfxjgtBFx0Zb8J/yGxaEl9JGg9j+e1JTcKywqu8","size":5953},"scripts/icons.ts":{"integrity":"sha384-W7so7VfFuLEVUMJGNxWA2kNXw5LPKL0D4ZkB+WU0pBp6fkM6NHwYDZHRaS3S4Gji","size":2306},"scripts/message-bus.ts":{"integrity":"sha384-BxGE3lQmg0OUPQpVtv7zQxnFG1AphUU2RpRcT0CaiIBduAZ+hpExXvd9gSj0dDkp","size":10293},"scripts/state.ts":{"integrity":"sha384-vNTXekH1jMcqiMauLtz6bZ2Yy5v8qwO66Jf0h6q+LIfGEhKVQsYPIgN/9i55bFlq","size":2000},"scripts/tabs/Moderation.tsx":{"integrity":"sha384-UsU9Wai6aff/kpLGiXtZAyHSEurtjb+B/umwgPRkYLmnqtQ2iSyeHrV2NKXg+Mlc","size":4717},"scripts/tabs/Rewards.tsx":{"integrity":"sha384-bKzO+bKEXJSdq3cDap8pn4KiCgm1gzqmf3Ei4ydcIrXCZA2KHmK7MeyGtQaOnlYP","size":4845},"scripts/tabs/SmartItemActions.tsx":{"integrity":"sha384-2EBxPB8FoidC82/EJIbfdj0WLZY9EM8DKIMfY6WcNkIJNazziDoT0c9TWLDQSgUu","size":6393},"scripts/tabs/TextAnnouncements.tsx":{"integrity":"sha384-auE1OM8qu77Mf+NJ0Q9phmWHoa/8hAELiRNzk0iMzh9p9P++K17e2r6n4wRu8V4q","size":6297},"scripts/tabs/VideoControl.tsx":{"integrity":"sha384-kb3f5pujq+a7c32PQSwUEOEPRfdjabLpJXnYsDCzDOepMmSPpIRpQ5ZEkbCDRajd","size":5199},"scripts/tabs/announcements/Overlay.tsx":{"integrity":"sha384-gwqwL5sPpKVyrX5zvXFl+spo+hOQuZ5Ao5HtwlcqNNjAszJtbZQZkRGoIeS91yQr","size":2933},"scripts/tabs/announcements/icons.ts":{"integrity":"sha384-ITtseCBy5n3WQxScQG0Y6LxNaD/L8CTPmI/lboI4QTMunXwHpmFmkW4SKHOgUnBn","size":455},"scripts/tabs/announcements/state.ts":{"integrity":"sha384-H8QR1U5I52xzN8kBd0JoMNmVPDXk+9h+G4Ophfhb413JPQgGUqva1XR1BC/ck8AN","size":2351},"scripts/tabs/announcements/ui.tsx":{"integrity":"sha384-PuNRzeUXeOVMuBO/sXnBrGEaB8n3qPofhnJOtC5FXz6EfnQ1T9wS63XPRa23GKRU","size":1072},"scripts/tabs/moderation/AddUserInput.tsx":{"integrity":"sha384-RiyCa08HtzCBd3VEe+tP6ntPOzHyb1F98RhXqkAwBKB14OT9puqedXekff7aGs37","size":5617},"scripts/tabs/moderation/RemoveAdminConfirmation.tsx":{"integrity":"sha384-tT43wHl1ywHy7tmio8nqzeZdr8kymxwHUfaPHoVc62u7ODx9GCygB4pu8jtHPRiX","size":2803},"scripts/tabs/moderation/UsersList.tsx":{"integrity":"sha384-0Wa4fy2crAQe39qgwbwdBwQSFswNGCrELHxCFZ1jBmxgh4R1HYKKDrFpAwtoq87/","size":7191},"scripts/tabs/moderation/api.ts":{"integrity":"sha384-Q/qkDk6GC53eutQvYwf4Lmyy3fPEH84nj1c+7JZDYEt0xrmGHgYPcXcfju2bWNii","size":1322},"scripts/tabs/moderation/icons.ts":{"integrity":"sha384-QqvX+zxVEVZojv5uHj0yl9aaSO3dPX8g7bvAizI7+jmUfT7Y919Bu6m1lbM33FYC","size":706},"scripts/tabs/moderation/kick.ts":{"integrity":"sha384-Rgr5yEmwEtTgZQirGmAOjGlJ2S9eBk35RwCgLTnkfvIQL4Q+ueU77sJ1nkeyPjfL","size":3965},"scripts/tabs/moderation/state.ts":{"integrity":"sha384-xvW2sWrMBcIr395zhA4Z/CJmvRzdtUJYIwJj4jBO0BcqYtaokEtRInseFPyfWU5A","size":1950},"scripts/tabs/moderation/ui.tsx":{"integrity":"sha384-s/X9vWTUhAfIZbzFtFwAJsjBjtBx7WcNpYOV1X5a2aruCkBB9zGqsRGF+k8ieVfW","size":5207},"scripts/tabs/moderation/utils.ts":{"integrity":"sha384-TAcs4ANF4RdzECa13ol7kWSKBtNZZZ5n+IydZlt9vl0FqZhnaw+MiBD2Q8gtacsU","size":3184},"scripts/tabs/rewards/Button.tsx":{"integrity":"sha384-Z66D7boMToX9iMvJr2cCAvA6EuxSPNKAMnIMeyry+xnokDOenJrmDaVBqX2pN6gt","size":1539},"scripts/tabs/rewards/Captcha.tsx":{"integrity":"sha384-H89W2CtQmpxF4LU6hLh409aHbadDb/Ukv5oRVc65e6kI+C7ixvCXPDtBjlerFnyo","size":2490},"scripts/tabs/rewards/api.ts":{"integrity":"sha384-T42JhF83QZuzIB06HAa09Bclq/qUwRvP/FwxhhAaVjuH7Ml0ehmiHgZDRYj95H5S","size":4796},"scripts/tabs/rewards/claim.ts":{"integrity":"sha384-pRPeSNGiGx9wb4+v35haHCGfWX+CBiMLu/1iVLitZ6pNhTzvBu24CisrFWmycQqM","size":3656},"scripts/tabs/rewards/controller.ts":{"integrity":"sha384-JPs8MvAVa6pGrdKlvoFr0H1P3FoQMIuiwwGcZMvkHC8kVVq4saX0x5T4RCIH35Rq","size":3368},"scripts/tabs/rewards/icons.ts":{"integrity":"sha384-S/7TaZ0mzvxhZJnxzt2ilqYAINoHo81Wh2vLRELZpmS/jQuL6KGBG9Tmw+COF1pb","size":275},"scripts/tabs/rewards/state.ts":{"integrity":"sha384-4PsbphYBjuf6o9Dnz8h6Iyp+s1LQrV1PYhteV0zpHz3nQFo8mZf8gRbphB+fpEdT","size":840},"scripts/tabs/smart-items/selection.ts":{"integrity":"sha384-lxSzgZrpbsomjarF9K1UhsfX1QuiPmeqMPY3jUBOG2/CU9i9ZCXrRPAexlhOaNiv","size":3331},"scripts/tabs/video/DclCast.tsx":{"integrity":"sha384-Rz/xL437EcTxwNCYvjbuHuw3klhBGL6N/Pb0hc6Vu+gjJVrLKnqRIo9hTEzeqWs6","size":8072},"scripts/tabs/video/LiveStream.tsx":{"integrity":"sha384-xJfNS7uAkczd6Mv7jI6kfVhA4cJQfbzUXZB+POKe7scklnpJJttNimcHHfl2sh5P","size":9886},"scripts/tabs/video/SpeakerShowcase.tsx":{"integrity":"sha384-5fEeVnMIEMYTQHztNlyULWEts6h/Zgv3kH5+/iQy9uIh5eNe1iqVtwArKrM0RuAP","size":3802},"scripts/tabs/video/VideoUrl.tsx":{"integrity":"sha384-pEnlEeG6qstYzljbAkeeFfQZilijueSfKmZFUPOzDC8I6K0ETrlf45hbbMX8V0fr","size":3426},"scripts/tabs/video/Volume.tsx":{"integrity":"sha384-q4odOzbD+Mx5BzotjtIx8+GQMGX9Ysy657573+S/bts+yrr1w0bMN91NtKzNc7Vf","size":2133},"scripts/tabs/video/api.ts":{"integrity":"sha384-UDwFd8lR+diOPOejRUq8Bk/DHs1OQN9nb7jNE2Nv+1KUm4JwUxu4b7LMExuWDSuJ","size":5673},"scripts/tabs/video/controls.ts":{"integrity":"sha384-8NTX/Mu0Ps34x4kxL2gwmJrbxyUVxYwT0pH5ig22+FGJU6UqHKIV2MyPZbJyBEWM","size":3886},"scripts/tabs/video/icons.ts":{"integrity":"sha384-3/0EHH0ZB0R4gcxXUlHvWFqKEoHx0WJyl7ZWh3sAGpIYGcJFT4OtqzeUkA0gRa4X","size":1662},"scripts/tabs/video/speakers.ts":{"integrity":"sha384-6YGpbWXwW6pSBAHC8o9P2Lu0P6IbhsRseHAgDG2eHWUCFd4wAXhdR6l/FYM7wrL4","size":1105},"scripts/tabs/video/state.ts":{"integrity":"sha384-yPWXz+LaGfDLrbO6xuEAe6whwGGtahVvvpOJmQsLGc+P4qopgoD9FSA9+xOxZP8I","size":2256},"scripts/tabs/video/ui.tsx":{"integrity":"sha384-ANXvEZ75Ur0nZyxxaHFOdr+NBGx3zD25r3s8M8dVb6osakUGjBKhRUEEWKo6JmpV","size":7076},"scripts/types.ts":{"integrity":"sha384-/ieszyQ/vz9IKpyeCLEw7Gae+8EWZVQMkcWmKWn6roV3KkEj34zY8n87R5w9HxIY","size":1902},"scripts/ui.tsx":{"integrity":"sha384-fUQUBEHzEB8SO6Mhl3Trm9vfgCogyEIoOiVmP+eVnE1qI/HK6lqOFsLcNZNe/Lqy","size":3216},"thumbnail.png":{"integrity":"sha384-sh8ip9U6LNItjm30RSrxl83x7aYdlbROEDnI49S5x+uj3rpx7ohWSFgVi3jXpv+4","size":11615}}},{"name":"announcer","data":"{\n \"id\": \"fb41d8d1-b842-437d-a830-f38379e74743\",\n \"name\": \"Announcer\",\n \"description\": \"Show a themed message when a player enters or leaves a chosen Trigger Area, or receive announcements from Game Flow.\",\n \"category\": \"custom\",\n \"group\": \"Multiplayer Server\",\n \"tags\": [\n \"multiplayer\",\n \"server\",\n \"announce\",\n \"message\",\n \"toast\",\n \"ui\"\n ],\n \"requiresSdk\": \"auth-server\",\n \"version\": \"1.3.0\",\n \"changelog\": [\n {\n \"version\": \"1.3.0\",\n \"notes\": \"Announcements take their place in the shared HUD layout at the bottom centre, narrow enough to stay clear of the phone touch buttons and of other HUDs.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release: server code anywhere in the scene calls game.broadcast('announce', { text }) and every player's client shows the line for a few seconds.\"\n },\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The placed item's Script card now shows the broadcast line that makes it speak, under its hold seconds and font size.\"\n },\n {\n \"version\": \"1.2.0\",\n \"notes\": \"Shared theme and a Trigger Area picker for local messages without writing code.\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n }\n}\n","files":{"ai.md":{"integrity":"sha384-D3Cv9CWg7ofWd+5NtZB9U0GLWnX/7Mbx2KN4QumC6l0qNu9t/6EXIVVuVJcY/7NS","size":3331},"composite.json":{"integrity":"sha384-3w+7KNTh3KLidBVPFpOr5uXPyrtZH1UA5l5yy+r0WC4QYO8+bFmvO10wY6Zk1osX","size":554},"data.json":{"integrity":"sha384-UiHAWkFaSxgHUM3ufcszXLhIXvc/PRRv7y2VMpJdBHNJ8C8Bzw7Yk40k9dW9Sabt","size":1217},"scripts/announcer.tsx":{"integrity":"sha384-cXTJST1CbTriSs6Vjg5CMhMzuGYX8Y5um8dH04rhrBrpMGI3/+rp7rF+OyiK6Bjs","size":1761},"scripts/pure/toast.ts":{"integrity":"sha384-5HsNX5Ao3DBwh0XwQx036b4r8OIvM4C7zYUAPHMVNPtEWb7kMdJASFEwKtxrb/jE","size":1171},"thumbnail.png":{"integrity":"sha384-HBZUcCEOa9jkubVyvd8Hrhnu3HU0OHKhshGy1LYXw0I6bANVnxcUy0psn8fuyqR6","size":2005}}},{"name":"black-chair","data":"{\n \"id\": \"1aa01433-9f2e-44ac-b1fb-a2bdffeb6776\",\n \"name\": \"Black Chair\",\n \"description\": \"A black dining chair. Players can sit on it.\",\n \"category\": \"custom\",\n \"tags\": [\n \"seat\",\n \"furniture\",\n \"sit\"\n ],\n \"version\": \"1.1.0\",\n \"changelog\": [\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The seat no longer runs on the Multiplayer Server, where it was walking every sit spot's transform chain each frame for nobody to see.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n },\n \"requiredPermissions\": [\n \"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE\",\n \"ALLOW_TO_TRIGGER_AVATAR_EMOTE\"\n ],\n \"group\": \"Seats\"\n}\n","files":{"HighBackChair.glb":{"integrity":"sha384-BQAU/CeS2nc/qMg4zNmGfN9PL54Vok9qKD681gVfC64knQyWW/wh2rGXNVc53oJ6","size":32424},"composite.json":{"integrity":"sha384-3/TVCIpJOzxplhEyfQhTIfMqAzc6J/jyo6qWRCI3iQMMSlSNnYS69w8+vBdNheC2","size":1413},"data.json":{"integrity":"sha384-zBCFwVRiankPHLu/0p80cZPNHuuPr8KywXQh+Co6MN1LWmPfYffNk2UvpyFc3WK2","size":699},"scripts/seat.ts":{"integrity":"sha384-qTc33dk7kx9+avQ4JugIv1csCyESSIOVst3FKQhN3pC0OIyeh/yA32h0z8zjdIWc","size":6959},"thumbnail.png":{"integrity":"sha384-bZsSYvZkaq1R6jGe8fK1yvdC1ws8F0nAtbKUpWRkpEretquQ1Pcx05H14NIobZA4","size":8744}}},{"name":"camera-rig","data":"{\n \"id\": \"7ea892f3-168f-40fb-8338-d9c91c23708e\",\n \"name\": \"Camera Rig\",\n \"description\": \"A fixed game camera — top-down, isometric or following the player — that takes over while a player is inside its area and frames the whole area on any screen, phones included.\",\n \"category\": \"custom\",\n \"tags\": [\n \"camera\",\n \"top-down\",\n \"isometric\",\n \"view\"\n ],\n \"version\": \"1.0.1\",\n \"changelog\": [\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release: a top-down, isometric or follow camera that takes over while a player is inside its area (or all the time), fits the whole area on desktop and phone screens, and hands the default camera back on the way out.\"\n },\n {\n \"version\": \"1.0.1\",\n \"notes\": \"The AI guide names genres in its examples, not a specific game.\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n }\n}\n","files":{"ai.md":{"integrity":"sha384-k09gDrZwz29ZhjBRnMhGaffnJVIbhIfsYWYYGNpuKLRyPOdbfzmGhOfeA/iLLmVX","size":3863},"composite.json":{"integrity":"sha384-ou9CI0Q1Mt9ikRHU0UUDqqrC1Y3E4dv/vSlrYzb2d9SZbesrFlrit1ulk0D/gJDT","size":915},"data.json":{"integrity":"sha384-ij6tcNkfBQXRqQU9uzjowdg1VnOpLDEC2uI9NrA4kQE6RaQw4lI1kW4OeuJD8TJW","size":861},"scripts/camera-rig.ts":{"integrity":"sha384-S/ZnswWj9Wh/IMJ6RL84LmU6H6B0JLLRB6mRB2ucG3unU2jAMBVHklNp0z4vZ7iA","size":7767},"thumbnail.png":{"integrity":"sha384-jkkA8fFCJKFnzNRruLLm6Snhpa/+0i224sx+HrpzEd6JGw1+fzITRRXnFyn0lSO3","size":16703}}},{"name":"classic-bench","data":"{\n \"id\": \"3e8dace7-01e4-4a98-a56e-52ad05ae654d\",\n \"name\": \"Classic Bench\",\n \"description\": \"A classic park bench, seating two. Players can sit on it.\",\n \"category\": \"custom\",\n \"tags\": [\n \"seat\",\n \"furniture\",\n \"sit\"\n ],\n \"version\": \"1.1.0\",\n \"changelog\": [\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The seat no longer runs on the Multiplayer Server, where it was walking every sit spot's transform chain each frame for nobody to see.\"\n },\n {\n \"version\": \"1.0.1\",\n \"notes\": \"Ships the model's texture, which was missing from the folder — without it the model rendered blank white in-world.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n },\n \"requiredPermissions\": [\n \"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE\",\n \"ALLOW_TO_TRIGGER_AVATAR_EMOTE\"\n ],\n \"group\": \"Seats\"\n}\n","files":{"Bench_01.glb":{"integrity":"sha384-qU+pwMwPJYsOMD3Rgh5aQ/XkBKR6ls6nQyqWaRik+z125N+cSW7f7tfaH6rQPVjz","size":54436},"composite.json":{"integrity":"sha384-KTMuUiX7SNG/qh3LQ9JANv/ql+YrUC3UHna5xa8GleFBEVOx2NTafDEFyjXPlUZL","size":2004},"data.json":{"integrity":"sha384-WzR61MlY+E4phm2i405fbw1qr2W5XZhV3elr6lpAIVX7ssoqTzxb8m3lTSKaqrLi","size":887},"file1.png":{"integrity":"sha384-hkHg23U+9Yx8eU+7OWRgOsHJ1gS8nzdWZ/DEAQvtwJF3dOf7sTQwINVaIo30FRk5","size":7117},"scripts/seat.ts":{"integrity":"sha384-qTc33dk7kx9+avQ4JugIv1csCyESSIOVst3FKQhN3pC0OIyeh/yA32h0z8zjdIWc","size":6959},"thumbnail.png":{"integrity":"sha384-gVZ5hbkqeZCkqlNp2XTBqGLqDMFBH261RyXCV/+xogCBddr593SpTijJcTG75xRw","size":10722}}},{"name":"classic-bench-armrests","data":"{\n \"id\": \"88e01dcc-b242-4815-90e3-72ff52c24f45\",\n \"name\": \"Classic Bench Armrests\",\n \"description\": \"A classic park bench with armrests, seating two. Players can sit on it.\",\n \"category\": \"custom\",\n \"tags\": [\n \"seat\",\n \"furniture\",\n \"sit\"\n ],\n \"version\": \"1.1.0\",\n \"changelog\": [\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The seat no longer runs on the Multiplayer Server, where it was walking every sit spot's transform chain each frame for nobody to see.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n },\n \"requiredPermissions\": [\n \"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE\",\n \"ALLOW_TO_TRIGGER_AVATAR_EMOTE\"\n ],\n \"group\": \"Seats\"\n}\n","files":{"Bench 1.glb":{"integrity":"sha384-gEvudQhcSvOjXcqAeY8D8iPaVbsYOkBd3YdZki2iAKCkZgkf/PQLaMW5DAJHFVZB","size":42772},"composite.json":{"integrity":"sha384-KuygG/7cDAvXqt2lz2mYDyeppiq1PZN+ZmBDndaWxdqV7csCPMdXQK/Ys40UW7YZ","size":2014},"data.json":{"integrity":"sha384-t19LA1qyWZc5sRylViJkgGHA8wo0wZqEVaA586BsRGQmzQc4itLDkgxE4LIdvHeR","size":737},"scripts/seat.ts":{"integrity":"sha384-qTc33dk7kx9+avQ4JugIv1csCyESSIOVst3FKQhN3pC0OIyeh/yA32h0z8zjdIWc","size":6959},"thumbnail.png":{"integrity":"sha384-u4mWUcX2kkrcgfIKydVN3kKeDu7K2Jfg9TKNBYavhhiIjZ0bWSUPVg9VABZZuaV1","size":19258}}},{"name":"classroom-chair","data":"{\n \"id\": \"cacd50dc-5861-4b71-83ee-83cb23fea9e4\",\n \"name\": \"Classroom Chair\",\n \"description\": \"A moulded classroom chair. Players can sit on it.\",\n \"category\": \"custom\",\n \"tags\": [\n \"seat\",\n \"furniture\",\n \"sit\"\n ],\n \"version\": \"1.1.0\",\n \"changelog\": [\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The seat no longer runs on the Multiplayer Server, where it was walking every sit spot's transform chain each frame for nobody to see.\"\n },\n {\n \"version\": \"1.0.1\",\n \"notes\": \"Ships the model's texture, which was missing from the folder — without it the model rendered blank white in-world.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n },\n \"requiredPermissions\": [\n \"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE\",\n \"ALLOW_TO_TRIGGER_AVATAR_EMOTE\"\n ],\n \"group\": \"Seats\"\n}\n","files":{"Chair_02.glb":{"integrity":"sha384-4sbNyI8nx8fgdkKm/Df7ooV8fEtA5sUTxy7VI2R+91f/JDklg4yFxOk4U3RXIFuo","size":16368},"composite.json":{"integrity":"sha384-Esh68GGVhUf9RCOfsDXWQaUiGb273cV7Z5cC2MOXe6JkUJknpHCpvyBwgPjvIHTg","size":1447},"data.json":{"integrity":"sha384-2FqLS4bQ26dRzDJZgb3HlB/Hrlt6x8fzSQnCoHVeGFIyZHcJ77N4WClJlt0iAYQB","size":881},"file1.png":{"integrity":"sha384-hkHg23U+9Yx8eU+7OWRgOsHJ1gS8nzdWZ/DEAQvtwJF3dOf7sTQwINVaIo30FRk5","size":7117},"scripts/seat.ts":{"integrity":"sha384-qTc33dk7kx9+avQ4JugIv1csCyESSIOVst3FKQhN3pC0OIyeh/yA32h0z8zjdIWc","size":6959},"thumbnail.png":{"integrity":"sha384-xp435rb3UtVrmBql5h82OUHv9+1HRv4msRquMkJF1ABAwAmE0htpneGPsIovG1dU","size":14671}}},{"name":"court-chair","data":"{\n \"id\": \"e29fe7dd-20f3-4486-941e-bb85401d0d1d\",\n \"name\": \"Court Chair\",\n \"description\": \"An upright court chair. Players can sit on it.\",\n \"category\": \"custom\",\n \"tags\": [\n \"seat\",\n \"furniture\",\n \"sit\"\n ],\n \"version\": \"1.1.0\",\n \"changelog\": [\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The seat no longer runs on the Multiplayer Server, where it was walking every sit spot's transform chain each frame for nobody to see.\"\n },\n {\n \"version\": \"1.0.1\",\n \"notes\": \"Ships the model's texture, which was missing from the folder — without it the model rendered blank white in-world.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n },\n \"requiredPermissions\": [\n \"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE\",\n \"ALLOW_TO_TRIGGER_AVATAR_EMOTE\"\n ],\n \"group\": \"Seats\"\n}\n","files":{"Chair_Wood_01.glb":{"integrity":"sha384-IIv1LpNQXbn42cpCwd4gsgDfThllJfAjB0pjMC51jcgUqJD6yJ25vIUv4OUSNZ0B","size":38776},"FanstasyPack_TX.png":{"integrity":"sha384-KMdE2Mz2QPnFyTWlJZWCUSxASSMsmyw/VFzHLyFQvXehN1ujIEepbskPuNqHgCI+","size":15078},"composite.json":{"integrity":"sha384-Q1IbQdubZR+c8womUaRcMywMD5GjYB/t46kuu309jIHimWA2YHGNgl/Ei5G/B1Bg","size":1408},"data.json":{"integrity":"sha384-akBy4ls/agEp39hMybq9vUIOyxV4KMBId+YLlayMWgc7QJcI/cLc+UktiHJOny6R","size":874},"scripts/seat.ts":{"integrity":"sha384-qTc33dk7kx9+avQ4JugIv1csCyESSIOVst3FKQhN3pC0OIyeh/yA32h0z8zjdIWc","size":6959},"thumbnail.png":{"integrity":"sha384-v2OuF/sRj8TMvjUqhYYJTkwfAMftvT//lNEFTULD99DFiWfc/bWhceAxxeo7emX+","size":12243}}},{"name":"curved-couch","data":"{\n \"id\": \"48dbe32d-c906-4287-bcfe-2cfe280c68f8\",\n \"name\": \"Curved Couch\",\n \"description\": \"A curved sectional couch, seating three. Players can sit along it.\",\n \"category\": \"custom\",\n \"tags\": [\n \"seat\",\n \"furniture\",\n \"sit\"\n ],\n \"version\": \"1.1.0\",\n \"changelog\": [\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The seat no longer runs on the Multiplayer Server, where it was walking every sit spot's transform chain each frame for nobody to see.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n },\n \"requiredPermissions\": [\n \"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE\",\n \"ALLOW_TO_TRIGGER_AVATAR_EMOTE\"\n ],\n \"group\": \"Seats\"\n}\n","files":{"CurvedCouch.glb":{"integrity":"sha384-EnJVY5fVNrynT2dL7huvsxVfZ5RbbvmrxnGjrEobubreqJ1U7qQo1ofzwBcNGYgg","size":38740},"composite.json":{"integrity":"sha384-Mx4dSJiZ3vo9xNctXyshiVZNlQ6SaYKDrtdLqpRT+ODzyJGneMwOxA/64u2rfqre","size":2013},"data.json":{"integrity":"sha384-7TJ7cPkU1KWPclbaj5CKLkVvIM6Ascfewj4ikqy9H6KRpu26ZyaBZfE6UekpP/ZU","size":722},"scripts/seat.ts":{"integrity":"sha384-qTc33dk7kx9+avQ4JugIv1csCyESSIOVst3FKQhN3pC0OIyeh/yA32h0z8zjdIWc","size":6959},"thumbnail.png":{"integrity":"sha384-oyKNtE22C6BjtcNTnnoBTYjfmIqVnik0SaM5IwfC1qheKHjXVa8Lxtihi9YDu8fs","size":14388}}},{"name":"game-flow","data":"{\n \"id\": \"44374100-12fb-481f-90ce-2a4d81949084\",\n \"name\": \"Game Flow\",\n \"description\": \"Lobby, countdown, rounds, winners — runs your game from start to end. Works with: Leaderboard, Announcer.\",\n \"category\": \"custom\",\n \"group\": \"Multiplayer Server\",\n \"tags\": [\n \"multiplayer\",\n \"server\",\n \"round\",\n \"lobby\",\n \"countdown\",\n \"timer\",\n \"winners\"\n ],\n \"requiresSdk\": \"auth-server\",\n \"version\": \"1.2.0\",\n \"changelog\": [\n {\n \"version\": \"1.2.0\",\n \"notes\": \"The 2D status panel takes its place in the shared HUD layout, so it no longer overlaps other HUDs on any screen.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release: lobby → round → winners on game.round, a countdown every client derives from one shared deadline, a winners line read from the board key a Leaderboard shows, and a \\\"script\\\" mode where your own game.newRound() ends the round and the round length is only a ceiling.\"\n },\n {\n \"version\": \"1.1.0\",\n \"notes\": \"Optional themed round timer and lobby countdown, configured in the inspector.\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n }\n}\n","files":{"ai.md":{"integrity":"sha384-FSQahckNpE6r400CC5Kw5Fz1uijUfaZ0N2XZBEm+YVMsbRTMfjX5zrlyKJMleUyl","size":4677},"composite.json":{"integrity":"sha384-TXN9bYBu1C79b9fTYJ2GbtOC1J4wjEpJt6rIykdYJTu3HEUw62il/8CIFSljcB4Q","size":932},"data.json":{"integrity":"sha384-cP7iRsJsHCRReEuJXncOHP41mG5PmbXB1BUnN0Kp16cOeLv+USBLanxLKbQNVY+U","size":1143},"scripts/game-flow.ts":{"integrity":"sha384-ALqBHmmmh5DxU2YGmpsDbjJyY3Lxh2rn2GBWGu0dlXOxXdG7h6NzhBlxdbVnkqYp","size":6524},"scripts/pure/flow.ts":{"integrity":"sha384-zEgjgIQS3RzCJ8Tyud8tywm5SmD+FO8WLnFpBkexnFEnmbOoWSb37HUx3X0/DLT3","size":7575},"thumbnail.png":{"integrity":"sha384-ZQHTd8rM4CaE2+qNC5at7B4H3e1ikf1ck9K6GI/TEwn/7WKzf2/9I9sInMvrxy8J","size":13227}}},{"name":"health-respawn","data":"{\n \"id\": \"5cc3b4d7-2a87-4825-9a53-e2b007bcff24\",\n \"name\": \"Health & Respawn\",\n \"description\": \"Players can take damage and respawn. Works with: Game Flow, Trigger Area.\",\n \"category\": \"custom\",\n \"group\": \"Multiplayer Server\",\n \"tags\": [\n \"player\",\n \"health\",\n \"damage\",\n \"respawn\",\n \"multiplayer\",\n \"server\"\n ],\n \"requiresSdk\": \"auth-server\",\n \"requiredPermissions\": [\n \"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE\"\n ],\n \"drivenBy\": {\n \"rule\": \"Nothing takes hit points by itself — the server calls damage() wherever the harm happens, and this item does the dying and the respawn.\",\n \"code\": \"game.onEnterArea('Spikes', (player) => damage(player, 25))\",\n \"next\": \"Press New script below, write that inside the isServer() block it comes with, and import damage from the health.ts beside this item's own script.\"\n },\n \"version\": \"1.3.1\",\n \"changelog\": [\n { \"version\": \"1.3.1\", \"notes\": \"The health bar is on by default again; turn Health Bar off for a game that never deals damage.\" },\n { \"version\": \"1.3.0\", \"notes\": \"Health bar setting, off by default: a course that never deals damage shows no hit points.\" },\n { \"version\": \"1.2.1\", \"notes\": \"Guide covers in-bounds respawn destinations, permissions and course playtesting.\" },\n {\n \"version\": \"1.2.0\",\n \"notes\": \"Per-player checkpoint destinations, healing, forced death, death counts, optional respawn delay and protection after respawning.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release: hit points the server owns in game.state.health, a respawn point picked in the inspector, a \\\"die below height\\\" death plane, and a damage() other server code calls.\"\n },\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The placed item's Script card now shows the damage() call that takes hit points, under its respawn settings.\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n }\n}\n","files":{"ai.md":{"integrity":"sha384-sOrhWgNJh7HnRDNRZnTFtj+lulT6lQpepsjcZ8+8S66IEKIbA/+TR0wineLeGJ0r","size":4755},"composite.json":{"integrity":"sha384-WZskIkQZ8n8dehUBe5OKQ+99aVon0PAX6Znh5z2TmWf177Wto80aE70iod28dvCF","size":565},"data.json":{"integrity":"sha384-/9l/nKLcx51jlRHD0Yafkl9meyPTAwCxTR8XteUkuH5jmmcfa8IK6SIiDJBhoqlp","size":1911},"scripts/health-respawn.ts":{"integrity":"sha384-QwLUhaAcxp9vui+9q7ccvuEpY87QHG+d/pzfAJRPn9L2yHhrvU605ORU21pz17hP","size":5760},"scripts/health.ts":{"integrity":"sha384-/zA9uuvRZ3uRS2WdFvPml83zRvA5n4h0RTNIEd2pb59Ui6yrtX1kJHj/zbK+Md4A","size":1981},"scripts/pure/health.ts":{"integrity":"sha384-if/V7vja1R5enh/H7yM4FCh3Ea7bgld+tr76ryjTWryrk3lEw7xFe6p282WsZFji","size":2230},"scripts/pure/life.ts":{"integrity":"sha384-oNHo7xAdJWVw3t5o7oDt+wjM9ea8FD/Ynskc2l1YjP3+qvnYLNnrI4v1pENPOorD","size":2368},"thumbnail.png":{"integrity":"sha384-fVCccAg9bAVrgYD6MFlGK+1zBO5XEDpxd0Lh7LGFaobZYl2OCaxJcME3eVrQqS31","size":7116}}},{"name":"high-stool","data":"{\n \"id\": \"f8ee0d2a-19f5-45a9-9029-cd46d7a8f834\",\n \"name\": \"High Stool\",\n \"description\": \"A tall bar stool. Players can sit on it.\",\n \"category\": \"custom\",\n \"tags\": [\n \"seat\",\n \"furniture\",\n \"sit\"\n ],\n \"version\": \"1.1.0\",\n \"changelog\": [\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The seat no longer runs on the Multiplayer Server, where it was walking every sit spot's transform chain each frame for nobody to see.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n },\n \"requiredPermissions\": [\n \"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE\",\n \"ALLOW_TO_TRIGGER_AVATAR_EMOTE\"\n ],\n \"group\": \"Seats\"\n}\n","files":{"HighStool.glb":{"integrity":"sha384-pELylPLTKad8MmTwL9+jXPGUf5KOnXgxBuY4JXguuov7duJEIVRk4pRcQ+gCgKmf","size":26332},"composite.json":{"integrity":"sha384-U3fv7ocM1aBZWJ+l9uC2Eg2acDqnoxy3tPhr8dXx1Vi7N1riaugAdQTDIpTQQyAK","size":1416},"data.json":{"integrity":"sha384-LjCLqThIrUN7Qk+zA6paffpTxFsPGIiDdRhVow2w9PlMxyX+EvyLO+tds9fHTX7O","size":694},"scripts/seat.ts":{"integrity":"sha384-qTc33dk7kx9+avQ4JugIv1csCyESSIOVst3FKQhN3pC0OIyeh/yA32h0z8zjdIWc","size":6959},"thumbnail.png":{"integrity":"sha384-Y4cPyTFXuHC5maHoDtEX7vdYSK76XLhw9JbnYPORlyOSmLAfhkvv/FjW0apjV9VS","size":9066}}},{"name":"large-couch","data":"{\n \"id\": \"ac233c16-699e-4f01-8e7c-910bb5a168af\",\n \"name\": \"Large Couch\",\n \"description\": \"A long couch, seating three. Players can sit along it.\",\n \"category\": \"custom\",\n \"tags\": [\n \"seat\",\n \"furniture\",\n \"sit\"\n ],\n \"version\": \"1.1.0\",\n \"changelog\": [\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The seat no longer runs on the Multiplayer Server, where it was walking every sit spot's transform chain each frame for nobody to see.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n },\n \"requiredPermissions\": [\n \"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE\",\n \"ALLOW_TO_TRIGGER_AVATAR_EMOTE\"\n ],\n \"group\": \"Seats\"\n}\n","files":{"ThreeSeater_Couch.glb":{"integrity":"sha384-Oo6LBiZPQO03QYxtyTqaG4NttfLoAeL14jtchqBbJjg1HQftbWUwrE1lQ8mSXCnM","size":31868},"composite.json":{"integrity":"sha384-HtdxTfaa5dyvF9BUlo0t2tzgSA4SWn3nwT2DccLeqnmscPpMqTKKmYt886lQaFYQ","size":2023},"data.json":{"integrity":"sha384-uIPJ6qQK7hXa2HghWSKPj5E8buDUVRb0XdeSUOcYoFnnQFTx3Nn9gWGf9m6kFTnU","size":709},"scripts/seat.ts":{"integrity":"sha384-qTc33dk7kx9+avQ4JugIv1csCyESSIOVst3FKQhN3pC0OIyeh/yA32h0z8zjdIWc","size":6959},"thumbnail.png":{"integrity":"sha384-/JwNvv3M/1qncoi0bQ7ZM9h7WQKP48Ti8KgAdHqtLxiwMogRZmWRhATdgF7nBz+3","size":16512}}},{"name":"leaderboard","data":"{\n \"id\": \"493daede-2afd-4c10-9c0d-6aefb25629a8\",\n \"name\": \"Leaderboard\",\n \"description\": \"Show game scores or automatic Run Progress best times on a 3D panel or themed screen overlay.\",\n \"category\": \"custom\",\n \"group\": \"Multiplayer Server\",\n \"tags\": [\n \"multiplayer\",\n \"server\",\n \"leaderboard\",\n \"score\",\n \"ranking\",\n \"board\"\n ],\n \"requiresSdk\": \"auth-server\",\n \"version\": \"1.6.0\",\n \"changelog\": [\n {\n \"version\": \"1.6.0\",\n \"notes\": \"The on-screen board takes its place in the shared HUD layout, so it no longer overlaps other HUDs, and shows the top 5 on phones, whose screen is a third shorter.\"\n },\n {\n \"version\": \"1.5.0\",\n \"notes\": \"Before the first score the board tells players \\\"No scores yet — be the first!\\\" (or \\\"No times yet\\\" for times) instead of setup instructions, and its text wraps to stay inside the panel. A board placed before this keeps its old text size, where the new line already fits.\"\n },\n {\n \"version\": \"1.4.0\",\n \"notes\": \"The 3D panel's text is readable from its front, and players can no longer walk through the board. A board placed before this keeps its old text position and no collider: delete it and place it again.\"\n },\n {\n \"version\": \"1.3.0\",\n \"notes\": \"Rows show the display name of players in the scene, or a name the row carries; others show a short address.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release: a panel that reads the game.state key you name in boardKey and paints its top rows, so the board a Script keeps is the board every player sees and a late joiner gets it from the snapshot.\"\n },\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The placed panel's Script card now shows the setState line that writes the rows it paints, under its own settings.\"\n },\n {\n \"version\": \"1.2.0\",\n \"notes\": \"Themed screen overlay and automatic persisted best times from Run Progress.\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n }\n}\n","files":{"ai.md":{"integrity":"sha384-mcZT0Ub5OZ/KpfDn6OvJ43I7ITFnl+OVGL7Y/IMznJc6cRuUKOu1VWmqVdyN5+hP","size":4523},"board-panel.glb":{"integrity":"sha384-Su82WkaQqG6obG/yGRkHw+WCXQCX/vMngfKyCV/IUH7tpdqz1MS2777hwjPMNdrI","size":1276},"composite.json":{"integrity":"sha384-sFuc9KWyDZpE2r3m+p3lj2YXw/hlALVOoguXm/wf1fSuN9T5pCXAlD7ow/9gOhQk","size":1916},"data.json":{"integrity":"sha384-TC0KbVHrPpPbhd+YP/5rmdZ7DRp6HfhUbpnEOV/8tiv+/ZPF4bRdzAAch2i36XCM","size":2009},"scripts/leaderboard-hud.tsx":{"integrity":"sha384-RKTVTqgYOCXUK/Ztei5exeXq/eeKgLJYoB6i+v9kwAJOiQsWNVJhCL7YOt9bC2kD","size":1789},"scripts/leaderboard.ts":{"integrity":"sha384-dyYY7XmhpYRYuoVvK+Gwn6iWbT7avt03iiNBuKK+5vyZPJpKBKzrv6ZiAolzayaW","size":4011},"scripts/pure/board.ts":{"integrity":"sha384-ks1PrCQkVaxlFeV4JEK7VUtIstr9rmU4dBqQLqN0QThlYOZ1StHQ5Vex+djFVxN9","size":3394},"thumbnail.png":{"integrity":"sha384-PqQ+DIWqDPRtuQIuK4NvtFcicWiCgQ94XqhBZWKGArKe+GEyHpjOdd9cerdgyamu","size":5768}}},{"name":"loveseat","data":"{\n \"id\": \"78050d8f-ee51-4db7-b060-14e7a0b48939\",\n \"name\": \"Loveseat\",\n \"description\": \"A two-seat sofa. Players can sit at either end.\",\n \"category\": \"custom\",\n \"tags\": [\n \"seat\",\n \"furniture\",\n \"sit\"\n ],\n \"version\": \"1.1.0\",\n \"changelog\": [\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The seat no longer runs on the Multiplayer Server, where it was walking every sit spot's transform chain each frame for nobody to see.\"\n },\n {\n \"version\": \"1.0.1\",\n \"notes\": \"Ships the model's texture, which was missing from the folder — without it the model rendered blank white in-world.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n },\n \"requiredPermissions\": [\n \"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE\",\n \"ALLOW_TO_TRIGGER_AVATAR_EMOTE\"\n ],\n \"group\": \"Seats\"\n}\n","files":{"Couch_01.glb":{"integrity":"sha384-Ybann6g/XRgUPIClpvLHX3e6SqETbaQ5vwLxVFADEhbMCc4YY3Jzw/RH6hR9gxSj","size":26236},"composite.json":{"integrity":"sha384-6gJtUvbDA9wZB5J/eUzFxTl2Ior4BViiorJCNokaAIZk/6/qXcg/7S1zGC+NsR6N","size":2005},"data.json":{"integrity":"sha384-uAOd4Pflpvbc2DFGnB3IX/xsDWHwz+RsG7UDDmuMluZHnB1bzIxFhw8+NEzh7T9/","size":872},"file1.png":{"integrity":"sha384-hkHg23U+9Yx8eU+7OWRgOsHJ1gS8nzdWZ/DEAQvtwJF3dOf7sTQwINVaIo30FRk5","size":7117},"scripts/seat.ts":{"integrity":"sha384-qTc33dk7kx9+avQ4JugIv1csCyESSIOVst3FKQhN3pC0OIyeh/yA32h0z8zjdIWc","size":6959},"thumbnail.png":{"integrity":"sha384-Y12yGhzQFK62sP07Qz6ipUPa++ZBer/QiCMMbpn1qnP/7n6RAJXoaDMYSGSvA4Hh","size":23593}}},{"name":"modern-sofa","data":"{\n \"id\": \"14706421-460b-4161-b06c-8ce7db73ad11\",\n \"name\": \"Modern Sofa\",\n \"description\": \"A low modern sofa, seating three. Players can sit along it.\",\n \"category\": \"custom\",\n \"tags\": [\n \"seat\",\n \"furniture\",\n \"sit\"\n ],\n \"version\": \"1.1.0\",\n \"changelog\": [\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The seat no longer runs on the Multiplayer Server, where it was walking every sit spot's transform chain each frame for nobody to see.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n },\n \"requiredPermissions\": [\n \"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE\",\n \"ALLOW_TO_TRIGGER_AVATAR_EMOTE\"\n ],\n \"group\": \"Seats\"\n}\n","files":{"ModernSofa.glb":{"integrity":"sha384-WT/qnGZedI5qRlBpLTndtxD90++WGbyQPpFeZJTWBDHR3555y88+LN4RG4aXLPA+","size":36796},"composite.json":{"integrity":"sha384-r6g/u0LMvBeUh8W3qMWZJ7H6OqgyqjNsPAL7jX5wOz+kvRwb178v0cD3YGoazd/T","size":2022},"data.json":{"integrity":"sha384-oMvACe5vrge+hMwWAsi9XHAe/OaFSCeWN8djSih9ET+M0j6CHHD5+tDxln/6crnw","size":714},"scripts/seat.ts":{"integrity":"sha384-qTc33dk7kx9+avQ4JugIv1csCyESSIOVst3FKQhN3pC0OIyeh/yA32h0z8zjdIWc","size":6959},"thumbnail.png":{"integrity":"sha384-+rS3k/5fjWqEHdGpcCs+Lur4gU1mESTaJ+fxHjpRjsKsUMbY1m6BP9G5VNwHtDea","size":16342}}},{"name":"outdoor-chair","data":"{\n \"id\": \"d8cb5c0f-dcf2-4fda-be37-2b76c4c0e78c\",\n \"name\": \"Outdoor Chair\",\n \"description\": \"A weathered outdoor chair. Players can sit on it.\",\n \"category\": \"custom\",\n \"tags\": [\n \"seat\",\n \"furniture\",\n \"sit\"\n ],\n \"version\": \"1.1.0\",\n \"changelog\": [\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The seat no longer runs on the Multiplayer Server, where it was walking every sit spot's transform chain each frame for nobody to see.\"\n },\n {\n \"version\": \"1.0.1\",\n \"notes\": \"Ships the model's texture, which was missing from the folder — without it the model rendered blank white in-world.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n },\n \"requiredPermissions\": [\n \"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE\",\n \"ALLOW_TO_TRIGGER_AVATAR_EMOTE\"\n ],\n \"group\": \"Seats\"\n}\n","files":{"Chair_07.glb":{"integrity":"sha384-rh67Oyki0AbHvwI7VAKxG4R6BvIZ+ApNvK3Ub3pjli95VKdib6IEUeCQIS5xRtIy","size":26632},"composite.json":{"integrity":"sha384-SdEMJ9948Cqop6/F49vW9182dpnKhA6sj/sGGfRC5+7NLSOvMk/wmxej4ptvjZwX","size":1419},"data.json":{"integrity":"sha384-erXzZe+COuUtzmtc67RGz02/w4ROQZMqgeU5TAsRJMcF0D1Pjj72jmzpKYJtjULa","size":879},"file1.png":{"integrity":"sha384-hkHg23U+9Yx8eU+7OWRgOsHJ1gS8nzdWZ/DEAQvtwJF3dOf7sTQwINVaIo30FRk5","size":7117},"scripts/seat.ts":{"integrity":"sha384-qTc33dk7kx9+avQ4JugIv1csCyESSIOVst3FKQhN3pC0OIyeh/yA32h0z8zjdIWc","size":6959},"thumbnail.png":{"integrity":"sha384-MuAlhtUctXmuZuYnp2/jzIowRuJoMj56xdErvUjb1WC8IJKidThtoJXtoDh1Mi2u","size":22220}}},{"name":"pickup-item","data":"{\n \"id\": \"3dd512ef-86ba-4ca2-8974-dc7629ded037\",\n \"name\": \"Pickup Item\",\n \"description\": \"Something players pick up with E (or the phone's action button), carry in the right hand, drop and throw. The Multiplayer Server decides who holds it, and the player sees it in hand at once. Works with: Work Station.\",\n \"category\": \"custom\",\n \"tags\": [\n \"pickup\",\n \"carry\",\n \"hold\",\n \"item\",\n \"ingredient\",\n \"throw\",\n \"multiplayer\"\n ],\n \"requiresSdk\": \"auth-server\",\n \"version\": \"1.0.0\",\n \"changelog\": [\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release: one holder at a time decided by the Multiplayer Server, held in the right hand and shown at once, a floating prompt with the device's own control, drop, optional throw, endless supply crates, and a script API to read, give and take what players carry.\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n }\n}\n","files":{"ai.md":{"integrity":"sha384-CzKBYLtOWD8mljnxlNig5FXr+fvWXIfxtSOHA5xWGcht/zTTS+cAO8upLSnaRqvn","size":3877},"composite.json":{"integrity":"sha384-2FkUxWGQrVSUBrsJ+VIOsWI6UM2xaB5Zwx24VZSuu9Eygn+gZHtHZs0+3Efb7hca","size":1118},"data.json":{"integrity":"sha384-SCVQG/tntqzcNSWqAx+xKsX+yM1Mq8i3uQlk7aus9g7PuLtjbOgcvmzoqfUzFTZh","size":897},"scripts/pickup-item.ts":{"integrity":"sha384-McRdI62+bnf+FbGoUaQUj8GkQq9ZQaqsMGexK30YHX9hkZbnwCd85jzgv49awInR","size":1712},"thumbnail.png":{"integrity":"sha384-2NzW1gGzZb7s0Dazwb5HwmzYU+SvssGabNn1GyXPh5tKslvz58F+6LRUfhmferWV","size":6826}}},{"name":"run-progress","data":"{\n \"id\": \"8ea22784-81dd-4d60-b0ae-54fef4852fb7\",\n \"name\": \"Run Progress\",\n \"description\": \"Timed runs with per-player checkpoints, collectibles, finish conditions, restart and a configurable HUD.\",\n \"category\": \"custom\",\n \"group\": \"Multiplayer Server\",\n \"tags\": [\n \"course\",\n \"race\",\n \"checkpoint\",\n \"collectible\",\n \"timer\",\n \"hud\",\n \"restart\"\n ],\n \"requiresSdk\": \"auth-server\",\n \"version\": \"1.4.0\",\n \"changelog\": [\n {\n \"version\": \"1.4.0\",\n \"notes\": \"The run HUD fits phones: Restart moves from the bottom-right corner (under the jump button) to under the clock, panels stack in the shared HUD layout instead of overlapping other HUDs, the controls hint matches the device, and the completion screen hides the touch controls while it is open.\"\n },\n {\n \"version\": \"1.3.1\",\n \"notes\": \"Hit points show unless Health & Respawn's Health Bar is turned off, so older Health & Respawn copies keep their bar.\"\n },\n {\n \"version\": \"1.3.0\",\n \"notes\": \"A missing spawn marker respawns players on its area's floor, not mid-air, and a checkpoint/marker count mismatch shows as a problem.\"\n },\n {\n \"version\": \"1.2.0\",\n \"notes\": \"HUD setting: Full, one big centred timer, or Hidden for a custom HUD. Health shows only when Health & Respawn turns its bar on.\"\n },\n {\n \"version\": \"1.1.0\",\n \"notes\": \"A missing spawn marker no longer stops the course: players respawn at the matching area, or at Health & Respawn's point.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Timed course runs with per-player checkpoints, collectible goals, restart, best times, a persisted leaderboard and a themed HUD.\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n }\n}\n","files":{"ai.md":{"integrity":"sha384-HEhvBW1B6ev3G9F3NJwBtkHfi3fuVS7ktxQLoQodEYlXnmA989fVh1/1U/RofXWw","size":6114},"composite.json":{"integrity":"sha384-VLC5nKAXOEJSV0e4pJv9kCp49u4nb6+OKeQImT7dYeDYMq3D21bDbiE79oyA4t0q","size":342},"data.json":{"integrity":"sha384-UrkVn6sNFnSOZ117kMO+A1RCAFy0BNX0VqJltOkoUtgeXkf6nnPPWSE3FyKUal4M","size":1751},"scripts/moving-platform.ts":{"integrity":"sha384-+OOc2vW73HOhsJCLElk2hGAcIJHfia3CKJbWkP7mOs0H7iAX3Gh5HYbcLUAZLXWh","size":1035},"scripts/run-progress.tsx":{"integrity":"sha384-qdQ+X5cdmZ4W5aogIDKuOghX0zGNiJkZE1zpq0F7iHUikOjDIX9Pe91Q4ghKjHw2","size":7645},"thumbnail.png":{"integrity":"sha384-6TEe0tNGVqIR8qvFRYv3jBgSHl7T2lZWQe6BuFofv7Lsfmwy2A6/ZHBc/Y1N6C2q","size":28248}}},{"name":"rustic-bench","data":"{\n \"id\": \"d6e264c5-a080-4702-827f-dcad4bfbc77c\",\n \"name\": \"Rustic Bench\",\n \"description\": \"A rough timber bench, seating two. Players can sit on it.\",\n \"category\": \"custom\",\n \"tags\": [\n \"seat\",\n \"furniture\",\n \"sit\"\n ],\n \"version\": \"1.1.0\",\n \"changelog\": [\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The seat no longer runs on the Multiplayer Server, where it was walking every sit spot's transform chain each frame for nobody to see.\"\n },\n {\n \"version\": \"1.0.1\",\n \"notes\": \"Ships the model's texture, which was missing from the folder — without it the model rendered blank white in-world.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n },\n \"requiredPermissions\": [\n \"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE\",\n \"ALLOW_TO_TRIGGER_AVATAR_EMOTE\"\n ],\n \"group\": \"Seats\"\n}\n","files":{"Bench_01.glb":{"integrity":"sha384-HvKGBqFZmpfflUDGmAv6bnKDgdLIRfY2gJbg+AffL+GjBuNBHpMsCpXWxfLXZDlN","size":38296},"PiratesPack_TX.png.png":{"integrity":"sha384-tvHmsChchDas/4FWyTySQozen2k/SnbQEzXKuV6p90FHRLZoH5NQyiDa9EOnNiHi","size":8600},"composite.json":{"integrity":"sha384-gGr5duM8zbC3xcX1ZPdVi5VH9qbExCukBk71DnHbpKDVp3R7BXD5Zi13eDIBoQE6","size":2007},"data.json":{"integrity":"sha384-7yCiT9oSixpR2SmV25CFxXVEFyPzmMI6/CMoXUIZp3vE1Vu0R591Vp6gjJalb/BE","size":886},"scripts/seat.ts":{"integrity":"sha384-qTc33dk7kx9+avQ4JugIv1csCyESSIOVst3FKQhN3pC0OIyeh/yA32h0z8zjdIWc","size":6959},"thumbnail.png":{"integrity":"sha384-ZmLePG5FIbmqf4RKCdidRKla6XAeP/+r51k4afbck7j8BR0RSwHEIDo7QWOSmmN2","size":11775}}},{"name":"rustic-round-stool","data":"{\n \"id\": \"80f58415-1e99-4ac3-ab21-7e4b93d8c1da\",\n \"name\": \"Rustic Round Stool\",\n \"description\": \"A round wooden stool. Players can sit on it.\",\n \"category\": \"custom\",\n \"tags\": [\n \"seat\",\n \"furniture\",\n \"sit\"\n ],\n \"version\": \"1.1.0\",\n \"changelog\": [\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The seat no longer runs on the Multiplayer Server, where it was walking every sit spot's transform chain each frame for nobody to see.\"\n },\n {\n \"version\": \"1.0.1\",\n \"notes\": \"Ships the model's texture, which was missing from the folder — without it the model rendered blank white in-world.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n },\n \"requiredPermissions\": [\n \"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE\",\n \"ALLOW_TO_TRIGGER_AVATAR_EMOTE\"\n ],\n \"group\": \"Seats\"\n}\n","files":{"Chairwood_01.glb":{"integrity":"sha384-UGMRBagdJnoipOK+RMQftg0R6vXgHD/6jTQJl45SOrw8uCclBWodtVxQj8lAwH97","size":26292},"PiratesPack_TX.png.png":{"integrity":"sha384-tvHmsChchDas/4FWyTySQozen2k/SnbQEzXKuV6p90FHRLZoH5NQyiDa9EOnNiHi","size":8600},"composite.json":{"integrity":"sha384-+vEWwi/GBNSbSKxucA5WevFwKuJvMkgdOzlA0UxB3vlTLjhfNr/db8qB9zLHoSsP","size":1423},"data.json":{"integrity":"sha384-+78WMw5CS6ay5o4yyZQC7P3dtmWW4rzjnd9j1FmyNJoAuHd4E0QeZH1bPJjJzIgZ","size":879},"scripts/seat.ts":{"integrity":"sha384-qTc33dk7kx9+avQ4JugIv1csCyESSIOVst3FKQhN3pC0OIyeh/yA32h0z8zjdIWc","size":6959},"thumbnail.png":{"integrity":"sha384-N3nz6ZelKTPqOpx5g6JLCNox5W71EwtvihYfFZffMoPDlR/oDSSVk2c44Wt/ABLH","size":13077}}},{"name":"rustic-square-stool","data":"{\n \"id\": \"9bc6d2f4-fb0a-4688-b81b-a4a83636193d\",\n \"name\": \"Rustic Square Stool\",\n \"description\": \"A square wooden stool. Players can sit on it.\",\n \"category\": \"custom\",\n \"tags\": [\n \"seat\",\n \"furniture\",\n \"sit\"\n ],\n \"version\": \"1.1.0\",\n \"changelog\": [\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The seat no longer runs on the Multiplayer Server, where it was walking every sit spot's transform chain each frame for nobody to see.\"\n },\n {\n \"version\": \"1.0.1\",\n \"notes\": \"Ships the model's texture, which was missing from the folder — without it the model rendered blank white in-world.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n },\n \"requiredPermissions\": [\n \"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE\",\n \"ALLOW_TO_TRIGGER_AVATAR_EMOTE\"\n ],\n \"group\": \"Seats\"\n}\n","files":{"Chairwood_02.glb":{"integrity":"sha384-IZcQfO7nBmiElbfMxtEy8WimKIB3WAefGB9cnz1A8oih4z6EuLuRleGbYwNr4DYZ","size":40548},"PiratesPack_TX.png.png":{"integrity":"sha384-tvHmsChchDas/4FWyTySQozen2k/SnbQEzXKuV6p90FHRLZoH5NQyiDa9EOnNiHi","size":8600},"composite.json":{"integrity":"sha384-Xqpw2Y5Vi9LBJsQf7uJIxPgt2ypqG7mIcjiQdnz2pAgbD9KKq88XHN22EW2vctRg","size":1424},"data.json":{"integrity":"sha384-pVW/W1QTcprakc0Yei7HEYqFpBmpDLHrzVANvYbipoPpWPf1rAI6rw4vFI9SUDys","size":881},"scripts/seat.ts":{"integrity":"sha384-qTc33dk7kx9+avQ4JugIv1csCyESSIOVst3FKQhN3pC0OIyeh/yA32h0z8zjdIWc","size":6959},"thumbnail.png":{"integrity":"sha384-8rf/o+kVZ7qhW/3D8hUzNanZVTfMvUMOB5+Zs85Tvn6EWobBD42Bvkpr2zuTMzbE","size":14789}}},{"name":"server-clock","data":"{\n \"id\": \"9e37c253-b33e-4505-8231-530f62715d21\",\n \"name\": \"Server Clock\",\n \"description\": \"A clock showing NTP-synced server time, identical for every player. Runs on the Multiplayer Server.\",\n \"category\": \"custom\",\n \"group\": \"Multiplayer Server\",\n \"tags\": [\n \"multiplayer\",\n \"server\",\n \"clock\",\n \"time\"\n ],\n \"requiresSdk\": \"auth-server\",\n \"version\": \"1.2.0\",\n \"changelog\": [\n {\n \"version\": \"1.2.0\",\n \"notes\": \"The 2D clock takes its place in the shared HUD layout, so it no longer overlaps other HUDs on any screen.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release: the Multiplayer Server's NTP-synced time, identical for every player, shown as 3D text at the item or as a 2D overlay on the player's client.\"\n },\n {\n \"version\": \"1.1.0\",\n \"notes\": \"Shared UI theme and independent clocks when multiple items use the overlay.\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n }\n}\n","files":{"ai.md":{"integrity":"sha384-/Awa1V4naWhY7vA66q5GEKJ4Aq7ZLOOx3xL9IKKVk5dKVMtA/MN9nWzHBA5FS5k1","size":3296},"composite.json":{"integrity":"sha384-3endi+ZKs5JfksNFuC3a+KvjsuCSyG9Ee23/mA/DKuwGPqtfLasLie3EiRd2Hqs/","size":907},"data.json":{"integrity":"sha384-6X6uh0Isy56LSYlumS2fWeQ+BL4ubUMofh/Ry7/ZF3hW2HpBdejep7t+o8bKffwt","size":955},"scripts/server-clock.ts":{"integrity":"sha384-TcnM9kQBRxlqHv6fFVkLLZ0JTEkaATZapl2FeZcCV6m0rxqtPN5VpGfzTU4vMA7W","size":2374},"thumbnail.png":{"integrity":"sha384-7vDACZ6Qb0G/LcQXW0w7DfZ/IyyT6NOmez0ZzJYgnwT9YECleSVRAHrXNZrWpVAo","size":8939}}},{"name":"simple-chair","data":"{\n \"id\": \"fe41823d-a0ef-4152-bf66-b9f44cdcffa2\",\n \"name\": \"Simple Chair\",\n \"description\": \"A simple lightweight chair. Players can sit on it.\",\n \"category\": \"custom\",\n \"tags\": [\n \"seat\",\n \"furniture\",\n \"sit\"\n ],\n \"version\": \"1.1.0\",\n \"changelog\": [\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The seat no longer runs on the Multiplayer Server, where it was walking every sit spot's transform chain each frame for nobody to see.\"\n },\n {\n \"version\": \"1.0.1\",\n \"notes\": \"Ships the model's texture, which was missing from the folder — without it the model rendered blank white in-world.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n },\n \"requiredPermissions\": [\n \"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE\",\n \"ALLOW_TO_TRIGGER_AVATAR_EMOTE\"\n ],\n \"group\": \"Seats\"\n}\n","files":{"Chair_03.glb":{"integrity":"sha384-Kr1BoWrtvWuVgiTcU0TmlB2hNVHJIOjoEXu27uAvSWezai6Nn+MYq9IkPAPTVjZy","size":10140},"composite.json":{"integrity":"sha384-Ah3wQrl5kn40r1Byv3A0InbOVhRgn9Z3559lyvQCCnFhRdPjR88/pngxS0lOWYlQ","size":1444},"data.json":{"integrity":"sha384-QcAWIr/jmyykog8qMz+2TmM7WCiqgbH6WpQA7iPbyx0cvKJRIvu/FpCOwFoeCy/P","size":879},"file1.png":{"integrity":"sha384-hkHg23U+9Yx8eU+7OWRgOsHJ1gS8nzdWZ/DEAQvtwJF3dOf7sTQwINVaIo30FRk5","size":7117},"scripts/seat.ts":{"integrity":"sha384-qTc33dk7kx9+avQ4JugIv1csCyESSIOVst3FKQhN3pC0OIyeh/yA32h0z8zjdIWc","size":6959},"thumbnail.png":{"integrity":"sha384-970of2uoK/Vy8uBQAQRDTI5ubtyaXF6NVB+QdweA/VzCaUyVlHTciY2bgnX4bbcs","size":10304}}},{"name":"sit-spot","data":"{\n \"id\": \"49bc2e26-dc56-430b-888e-509d56ae79ed\",\n \"name\": \"Sit Spot\",\n \"description\": \"An invisible seat: drop it on any surface and players can sit exactly there, facing the way it points.\",\n \"category\": \"custom\",\n \"tags\": [\n \"seat\",\n \"furniture\",\n \"sit\"\n ],\n \"version\": \"1.1.0\",\n \"changelog\": [\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The seat no longer runs on the Multiplayer Server, where it was walking every sit spot's transform chain each frame for nobody to see.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n },\n \"requiredPermissions\": [\n \"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE\",\n \"ALLOW_TO_TRIGGER_AVATAR_EMOTE\"\n ],\n \"group\": \"Seats\"\n}\n","files":{"composite.json":{"integrity":"sha384-uhBFFGia5mFzOQWC/J9UxtsSaldLqPEHiwzfnBYPzokXoME91oNDnBhwjPMNjpNc","size":799},"data.json":{"integrity":"sha384-ZpWPifWI1oc5/I6vlAeNNosb8cmuu5IEKnh/rdh8vdf57V+wnNsuVqAuFYn5aVdG","size":754},"scripts/seat.ts":{"integrity":"sha384-qTc33dk7kx9+avQ4JugIv1csCyESSIOVst3FKQhN3pC0OIyeh/yA32h0z8zjdIWc","size":6959},"thumbnail.png":{"integrity":"sha384-OrhbCwB59TLqDClVqt8Hrcy+OPXXkctZ1vW7zQTWOSaEWnyNRyne5SBtsVrZKVt2","size":18923}}},{"name":"sit-spot-edge","data":"{\n \"id\": \"6c815b58-d13f-4d30-a7ea-0f275d740084\",\n \"name\": \"Edge Sit Spot\",\n \"description\": \"An invisible seat for ledges and edges — the player sits with their legs hanging over the side.\",\n \"category\": \"custom\",\n \"tags\": [\n \"seat\",\n \"furniture\",\n \"sit\",\n \"edge\",\n \"ledge\"\n ],\n \"version\": \"1.1.0\",\n \"changelog\": [\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The seat no longer runs on the Multiplayer Server, where it was walking every sit spot's transform chain each frame for nobody to see.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n },\n \"requiredPermissions\": [\n \"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE\",\n \"ALLOW_TO_TRIGGER_AVATAR_EMOTE\"\n ],\n \"group\": \"Seats\"\n}\n","files":{"composite.json":{"integrity":"sha384-us527Hlk6/JM3bpEqqwHclf7+Xu8hXGrtrhZJ1w/atuO4025ZqJO80d0Ov4XIXnq","size":764},"data.json":{"integrity":"sha384-CH6sS8qFyE2hi/eP5h6IP+XIhtaTkrANQsfRDKzipE1+a3/anhHvfoXa66xmW6g3","size":779},"emotes/Sit_Edge1_emote.glb":{"integrity":"sha384-8oYD8S4HilS8mFsgOj5lRbBWoq3rmF7ETStGkvefUODtwN+lo5gxdKY/F80aqLIj","size":99400},"emotes/Sit_Edge2_emote.glb":{"integrity":"sha384-bS47w9rNkTXOUXmHoSOTkM8g+3AcRbSRLwGblftdTa2Hr0avdubdOdiC2D5bkM2E","size":89300},"scripts/seat.ts":{"integrity":"sha384-XonZTJcIqIC//h6SCTzpxOTs+MfpK2IHjkDTg1t+XmUUSZ7777snXkZeRzpahO7Q","size":7017},"thumbnail.png":{"integrity":"sha384-OrhbCwB59TLqDClVqt8Hrcy+OPXXkctZ1vW7zQTWOSaEWnyNRyne5SBtsVrZKVt2","size":18923}}},{"name":"small-couch","data":"{\n \"id\": \"acca7441-52c9-4b80-83fb-af18bcbf4c7e\",\n \"name\": \"Small Couch\",\n \"description\": \"A compact couch, seating two. Players can sit at either end.\",\n \"category\": \"custom\",\n \"tags\": [\n \"seat\",\n \"furniture\",\n \"sit\"\n ],\n \"version\": \"1.1.0\",\n \"changelog\": [\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The seat no longer runs on the Multiplayer Server, where it was walking every sit spot's transform chain each frame for nobody to see.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n },\n \"requiredPermissions\": [\n \"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE\",\n \"ALLOW_TO_TRIGGER_AVATAR_EMOTE\"\n ],\n \"group\": \"Seats\"\n}\n","files":{"TwoSeater_Couch.glb":{"integrity":"sha384-3aFVc1VRYCZMbuV54qKD37CG/Gyy8g4KkdPSjqqbmNMVELB8RVdCVn4bhaaJeTIA","size":35116},"composite.json":{"integrity":"sha384-4SsVtae9U3jtnd8ws0MFI6eEqXQA+K9d0YanHazJzExwBdh4yL/10B6oDCa5m9OE","size":2006},"data.json":{"integrity":"sha384-u6WCss6xFK0tfHpKnL40yNXkaJg1VNGwvL6RdTYzaSWbFZGSOUHJh0fK6CZhN4Io","size":715},"scripts/seat.ts":{"integrity":"sha384-qTc33dk7kx9+avQ4JugIv1csCyESSIOVst3FKQhN3pC0OIyeh/yA32h0z8zjdIWc","size":6959},"thumbnail.png":{"integrity":"sha384-Vo9jNKeVjbITSNCMK5+1FvD6QgI6prs0AZptiMN1LybCqlnhS3syD/yd7o6Dhos9","size":15304}}},{"name":"spawner","data":"{\n \"id\": \"fc759fc0-57a1-461c-aa06-22c0d3d4c3e0\",\n \"name\": \"Spawner\",\n \"description\": \"Makes copies of a prefab appear while playing — on a click, when a player walks in, on a timer, or when another script asks. Copies appear at the Spawner's own spot, only on this player's client.\",\n \"category\": \"custom\",\n \"tags\": [\n \"spawner\",\n \"spawn\",\n \"pickup\",\n \"enemy\"\n ],\n \"version\": \"1.0.0\",\n \"changelog\": [\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release: copies of a prefab appear while playing — on a click, when a player walks in, on a timer, or when another Script asks — made on that player's own client, with a per-spot cap, a lifetime and scatter.\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n }\n}\n","files":{"ai.md":{"integrity":"sha384-z0Sma23V3knVRWNDZMCDl+EKMeObpA2NxJzO543HDEWXdjM8rq2BUvW8ivVC3s2r","size":5423},"composite.json":{"integrity":"sha384-11bTytc5FvYc2S+noZUzFBRPTTCZ+JK3gMOGtS1vg57/Aqv0jIG+H6StFjCHbr+o","size":1383},"data.json":{"integrity":"sha384-te+nomjlQGpKcP1TUxjeJBvcNMY2e/q67l6VHqU0B81p9tCWgjz8qTps7gUZ2kdf","size":747},"scripts/spawner.ts":{"integrity":"sha384-2BCX+ZtBU07fyYb1gxapwzivM8DRe8i4CetRZgpVN8N5ta0vlxxFEDdfHUmEYOkJ","size":16034},"thumbnail.png":{"integrity":"sha384-4nU0PNVHtK4o6zj4kZ7zbNlQzczBQKUQPC3F8ZPrcP7BWYbExlEs3VlFajqKPwZG","size":6786}}},{"name":"steampunk-bench","data":"{\n \"id\": \"8a80b964-1f4d-4ea3-8f6b-078809891a17\",\n \"name\": \"Steampunk Bench\",\n \"description\": \"A riveted steampunk bench, seating two. Players can sit on it.\",\n \"category\": \"custom\",\n \"tags\": [\n \"seat\",\n \"furniture\",\n \"sit\"\n ],\n \"version\": \"1.1.0\",\n \"changelog\": [\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The seat no longer runs on the Multiplayer Server, where it was walking every sit spot's transform chain each frame for nobody to see.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n },\n \"requiredPermissions\": [\n \"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE\",\n \"ALLOW_TO_TRIGGER_AVATAR_EMOTE\"\n ],\n \"group\": \"Seats\"\n}\n","files":{"Steampunk Bench.glb":{"integrity":"sha384-l8rvgt9Z8JKpRwRI3rxw9qoH/vx1v7HvwxJoQWJi/xQ2v3+HIFzUE3SEMakC13lj","size":159244},"composite.json":{"integrity":"sha384-qT/zOtPEwpaAhxx6YNaSnoFHWNCzk9cluTzkbWVOT1ASokLY9hfUd+lhKDF6w/np","size":2019},"data.json":{"integrity":"sha384-ywBxOVAXX1Eo07MEC1qn5iwpz4Wxa0Sf9JHs8cYCOa0JL8D5/M0pRS/k2RuNCzD0","size":721},"scripts/seat.ts":{"integrity":"sha384-qTc33dk7kx9+avQ4JugIv1csCyESSIOVst3FKQhN3pC0OIyeh/yA32h0z8zjdIWc","size":6959},"thumbnail.png":{"integrity":"sha384-NNFHgxb81gJ0y4Z1APmww5ZHZz4My+jv53ogLyEKS+qjno5TAMnoSE4PFSELuGsR","size":16622}}},{"name":"steampunk-chair","data":"{\n \"id\": \"0cdcf518-9bb8-454e-9b18-a851b2a0b1df\",\n \"name\": \"Steampunk Chair\",\n \"description\": \"A brass-and-leather steampunk chair. Players can sit on it.\",\n \"category\": \"custom\",\n \"tags\": [\n \"seat\",\n \"furniture\",\n \"sit\"\n ],\n \"version\": \"1.1.0\",\n \"changelog\": [\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The seat no longer runs on the Multiplayer Server, where it was walking every sit spot's transform chain each frame for nobody to see.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n },\n \"requiredPermissions\": [\n \"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE\",\n \"ALLOW_TO_TRIGGER_AVATAR_EMOTE\"\n ],\n \"group\": \"Seats\"\n}\n","files":{"Steampunk Chair.glb":{"integrity":"sha384-jx1WnrmD7GigwvCDb4X6HDhs3Q4rc7J10DlakX2I31xP/64hO8E76U6nCARYu1D/","size":137292},"composite.json":{"integrity":"sha384-C9XF/cSfbX7UfnePwSOg2BrpL9m3nB5WyQRJkINrSOsmV+JoEcEAEv/2bl7pl9Kd","size":1420},"data.json":{"integrity":"sha384-qoBLKLZCSylATy78bslEs1GaUl+YjZPvDh4GBtS4cAkM5JzvXbQMX2QxWjOapOAU","size":718},"scripts/seat.ts":{"integrity":"sha384-qTc33dk7kx9+avQ4JugIv1csCyESSIOVst3FKQhN3pC0OIyeh/yA32h0z8zjdIWc","size":6959},"thumbnail.png":{"integrity":"sha384-tAJBIbkGkYJOtHKoWzPwIUmcjypRoYnqLu5S7mLiuQ5KUiHKZ493plJyalIZ5aYH","size":22358}}},{"name":"tall-stool","data":"{\n \"id\": \"f83a91fb-b939-4251-a298-d334c3e48996\",\n \"name\": \"Tall Stool\",\n \"description\": \"A tall stool with a footrest. Players can sit on it.\",\n \"category\": \"custom\",\n \"tags\": [\n \"seat\",\n \"furniture\",\n \"sit\"\n ],\n \"version\": \"1.1.0\",\n \"changelog\": [\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The seat no longer runs on the Multiplayer Server, where it was walking every sit spot's transform chain each frame for nobody to see.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n },\n \"requiredPermissions\": [\n \"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE\",\n \"ALLOW_TO_TRIGGER_AVATAR_EMOTE\"\n ],\n \"group\": \"Seats\"\n}\n","files":{"Stool_D.glb":{"integrity":"sha384-H8ztvhYutHrOV6dbXUfUSODKyMLIy/P5eEntuQxEe2zEHwMLnlQVfvqFRhWMtA+h","size":320312},"composite.json":{"integrity":"sha384-RnEdGr2iP41goFt9Gmfi5bYDprFCHPslIt+zDWXP25ohLUd8p2RO8Q6Q3fEm91w5","size":1412},"data.json":{"integrity":"sha384-C8mP3L8l99WT0GJixLoSzlekZHxR+orHexOZdET85GqVMSpbbRLCqQ6KRn9r8mjR","size":706},"scripts/seat.ts":{"integrity":"sha384-qTc33dk7kx9+avQ4JugIv1csCyESSIOVst3FKQhN3pC0OIyeh/yA32h0z8zjdIWc","size":6959},"thumbnail.png":{"integrity":"sha384-/VHtkn6ezh/nPA9bGkGF7eNlD7G/e+32phC0qmZ0/LSbEtn+jo5mWb+1RfzomHQW","size":11715}}},{"name":"trigger-zone","data":"{\n \"id\": \"f1794ec8-ed62-42c8-a71b-6c52e04b161a\",\n \"name\": \"Trigger Area\",\n \"slug\": \"trigger_zone\",\n \"description\": \"An invisible area that knows who is standing in it. Name it, scale it with the gizmo, then have a script react to that name.\",\n \"category\": \"custom\",\n \"tags\": [\n \"trigger\",\n \"area\",\n \"interactivity\"\n ],\n \"drivenBy\": {\n \"rule\": \"The area only publishes who is standing in it — a Script reacts to its name through './runtime/zoneBus', on this item or on any other entity in the scene.\",\n \"code\": \"onZone('Front Door', 'enter', (event) => { /* what happens */ })\",\n \"next\": \"Use the Add a reaction button below and the new Script arrives already listening to this area.\"\n },\n \"version\": \"1.1.1\",\n \"changelog\": [\n { \"version\": \"1.1.1\", \"notes\": \"Guide covers model bounds, active parcels and course prefab integration.\" },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release: an invisible named area that publishes who is standing in it, so a Script anywhere in the scene reacts to it by name with nothing wired.\"\n },\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The placed area's Script card now shows the onZone line a Script reacts with, under its area settings.\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n }\n}\n","files":{"ai.md":{"integrity":"sha384-D++yhIulqWPCrKizIyGTqDJF2ZxBv5GUgm9n8tbZKFO2xiwWUs8H5jMl49jSr+GK","size":6096},"composite.json":{"integrity":"sha384-VWkNF79zDduwdioBw5OubcoLdmnXzzZq6GXkq3u3k5qwD4JDktJw+wiLhI3Hzto+","size":917},"data.json":{"integrity":"sha384-r3pReo7kADyb6lmgav6cxgC2hwAVQoEGbLaINBkvAgkObtAk517Tf/GGZPI9Ocov","size":1286},"scripts/trigger-zone.ts":{"integrity":"sha384-57lvbBOHUXnIGndfQD1G9oRA9gVOpLg7JWwSAV9LJzeD6xGyTKCPhuLai3nhXE9R","size":3837},"thumbnail.png":{"integrity":"sha384-4htEAGokLp2yZKaMa5PZanI+3zqPe3TnLcXpmfyJjfnXhYmij+KVgZ51pXdeTnXN","size":4283}}},{"name":"ui-theme","data":"{\n \"id\": \"41939776-72b2-45f1-b40a-097fa66c9790\",\n \"name\": \"UI Theme\",\n \"description\": \"One accent color and appearance for game HUDs, timers, leaderboards, buttons, results and announcements.\",\n \"category\": \"custom\",\n \"group\": \"Scene UI\",\n \"tags\": [\n \"ui\",\n \"theme\",\n \"color\",\n \"hud\",\n \"design\"\n ],\n \"version\": \"1.1.0\",\n \"changelog\": [\n {\n \"version\": \"1.1.0\",\n \"notes\": \"Custom HUDs are placed through the shared HUD layout, which keeps them clear of the phone touch controls and of each other.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"One accent color and dark or light appearance shared by the scene's game HUDs, buttons, timers, leaderboards and results.\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n }\n}\n","files":{"ai.md":{"integrity":"sha384-nxOv4VlTrgwQXS1doZ8l2Ka0Lf+49wlK5fZe6IeN6ym0d49eVorEF0pkP+mW0zup","size":2263},"composite.json":{"integrity":"sha384-GLLTfiifHN/3BNFuV3tozHl5PqIr2Sw/1lsM/jQQ8BcUY8FIt6gPCUgxcloJaxsW","size":551},"data.json":{"integrity":"sha384-gPrFuwJDKwYj8TGgJIkhTtOc+M6nawLruchbna5wTiCJ+3yXQQ3HlLyc/Ck0xpB8","size":766},"scripts/ui-theme.ts":{"integrity":"sha384-InUblgjcHMPeDZ/IKir/gTVfws6YE4FkR/SUd0AA285wV7rJUfEorIxCetjIiXNj","size":420},"thumbnail.png":{"integrity":"sha384-Q+XsR3csSrtPv1vWF2KsovKOoh15mPBgVsBKlMdNnjJSOJi4JhhkrPZEBpjdzjRR","size":5983}}},{"name":"video-screen","data":"{\n \"id\": \"37188734-4562-4f21-afeb-2752c39278f4\",\n \"name\": \"Video Screen\",\n \"description\": \"A flat screen that plays a video file or a live stream, with volume and playback controls.\",\n \"category\": \"custom\",\n \"tags\": [\n \"video\",\n \"screen\",\n \"stream\"\n ],\n \"version\": \"1.0.0\",\n \"changelog\": [\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n }\n}\n","files":{"composite.json":{"integrity":"sha384-PNwL0kv1USZnEFOSnC3oF6kLaG3Hy0S5VGGvk16R+dkdkIidEPn40/SpRI4Eipsm","size":2536},"data.json":{"integrity":"sha384-CWXT5tqC3clzqs6wTwRMXrxBBT8PdHaNRpa4H4SFhZBVw4GfOe9/vmdjzk58KTaK","size":427},"thumbnail.png":{"integrity":"sha384-OQnhEX0GsIogArcrXaPYIo5bRqaYsK7V6GnniDUiiHDPOmojTFQKbcopVX8poMbd","size":3919},"video_player.glb":{"integrity":"sha384-Su82WkaQqG6obG/yGRkHw+WCXQCX/vMngfKyCV/IUH7tpdqz1MS2777hwjPMNdrI","size":1276}}},{"name":"wooden-chair","data":"{\n \"id\": \"c0f3a3f1-7fad-47bf-a855-e27f41f7894f\",\n \"name\": \"Wooden Chair\",\n \"description\": \"A plain wooden chair. Players can sit on it.\",\n \"category\": \"custom\",\n \"tags\": [\n \"seat\",\n \"furniture\",\n \"sit\"\n ],\n \"version\": \"1.1.0\",\n \"changelog\": [\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The seat no longer runs on the Multiplayer Server, where it was walking every sit spot's transform chain each frame for nobody to see.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n },\n \"requiredPermissions\": [\n \"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE\",\n \"ALLOW_TO_TRIGGER_AVATAR_EMOTE\"\n ],\n \"group\": \"Seats\"\n}\n","files":{"Chair 1.glb":{"integrity":"sha384-OBKh/Bj43q8Sx1wRponwlbgpevgaWMUiN0giUov/12Oi6zrbOTERiktleBfycvzW","size":34444},"composite.json":{"integrity":"sha384-Bl50OjO++oWtYNycDeONS6QDpxb6NUTuEcNYezYybT6QL2mKs1e+Ar7bZi9C8vQ7","size":1411},"data.json":{"integrity":"sha384-5LcT4SUEiXzUl7Fm686bC0qwplC+5wqCoijTQxWYqrKqiayYS6IBeCF8cfvayJS9","size":700},"scripts/seat.ts":{"integrity":"sha384-qTc33dk7kx9+avQ4JugIv1csCyESSIOVst3FKQhN3pC0OIyeh/yA32h0z8zjdIWc","size":6959},"thumbnail.png":{"integrity":"sha384-85oGX4HxIuc6wLmDYZYFS2Et0rxNcuex0sgvRhWn/hG3xXfMjbhWHOq16rkU629x","size":18385}}},{"name":"wooden-chair-armrests","data":"{\n \"id\": \"ebd62f4c-fa46-489f-bf8e-104aafc10b0f\",\n \"name\": \"Wooden Chair Armrests\",\n \"description\": \"A wooden chair with armrests. Players can sit on it.\",\n \"category\": \"custom\",\n \"tags\": [\n \"seat\",\n \"furniture\",\n \"sit\"\n ],\n \"version\": \"1.1.0\",\n \"changelog\": [\n {\n \"version\": \"1.1.0\",\n \"notes\": \"The seat no longer runs on the Multiplayer Server, where it was walking every sit spot's transform chain each frame for nobody to see.\"\n },\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n },\n \"requiredPermissions\": [\n \"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE\",\n \"ALLOW_TO_TRIGGER_AVATAR_EMOTE\"\n ],\n \"group\": \"Seats\"\n}\n","files":{"Chair 3.glb":{"integrity":"sha384-qC94v3lBqcsvDYKbxOFSBG9sXvO2WlwJ4CZlUVUKmTvKvsn94KetxdVRfO/8VJ9Z","size":40316},"composite.json":{"integrity":"sha384-8c+9B2WZVYU90TjDED4IskWU7oRBdYvk29okBEMaheh/V6vRP82wbUlM2syOB48g","size":1426},"data.json":{"integrity":"sha384-olI3fBaKG8LfISwoMdmldQE4DxzqD2THTl9YwQlF85f4L7M+FU/6uPTx1i/vs+C0","size":717},"scripts/seat.ts":{"integrity":"sha384-qTc33dk7kx9+avQ4JugIv1csCyESSIOVst3FKQhN3pC0OIyeh/yA32h0z8zjdIWc","size":6959},"thumbnail.png":{"integrity":"sha384-g1sgPvNl8JeOjulBezQcWzMoT1bkFgk/dNp9BfPv1SEQWa8dABvHvh4IDDnrKnlG","size":16240}}},{"name":"work-station","data":"{\n \"id\": \"500b804b-8d35-420a-b323-243df2e0eaa9\",\n \"name\": \"Work Station\",\n \"description\": \"A spot where a carried item is put down and taken back, and optionally worked on: hold F (or the phone's second button) to chop or wash it, or let it cook by itself, with a progress bar over it. Works with: Pickup Item.\",\n \"category\": \"custom\",\n \"tags\": [\n \"station\",\n \"counter\",\n \"craft\",\n \"cook\",\n \"chop\",\n \"progress\",\n \"multiplayer\"\n ],\n \"requiresSdk\": \"auth-server\",\n \"version\": \"1.0.1\",\n \"changelog\": [\n {\n \"version\": \"1.0.0\",\n \"notes\": \"Initial release: put down and take back one carried item, hold-to-work and timed modes that turn one kind into another with a progress bar, combining two kinds, and station events a script reacts to by the station's Name.\"\n },\n {\n \"version\": \"1.0.1\",\n \"notes\": \"The AI guide names genres in its examples, not a specific game.\"\n }\n ],\n \"origin\": {\n \"source\": \"builtin\"\n }\n}\n","files":{"ai.md":{"integrity":"sha384-hC9b78IOReSOQ9kuOPS3eF68+tLwPIB+qiHDOeH+J48P+KP7aDfPz5NQuDaLGgGs","size":3977},"composite.json":{"integrity":"sha384-o11OXBbs8puE3EBpzZP8RH2je6ApNF5Y8cbiVBvEosI/iFR/wME+hFImnGHPaHBa","size":1360},"data.json":{"integrity":"sha384-3PSuKyY1spU7JRXtxpYdAsoXu1Ba6VgJHxvCOT/dVeqZiqslFX1FkQemOEDwafsh","size":975},"scripts/work-station.ts":{"integrity":"sha384-xM18mqGzqGNv7xJ93IlP+B2/vSiQZjSrT+g2liniGgB0NY5cVHSaATVRdJqVt9S5","size":1798},"thumbnail.png":{"integrity":"sha384-WdzqTBASLJKRMCT5llVoVkuIrSoQL3cyutkNphHorfgVsTVoH9lY9rqlIJubWfkJ","size":6483}}}]
@@ -59,7 +59,7 @@ the module header there: `onStation(name, fn)` for put/take/combine/done events,
59
59
 
60
60
  ## Example
61
61
 
62
- "Overcooked-style: chop tomatoes and serve them for points". Place:
62
+ "A co-op cooking game: chop tomatoes and serve them for points". Place:
63
63
  Pickup Item "Tomato Crate" (`kind` "Tomato", `supply` "endless"); Pickup Item
64
64
  "Chopped Tomato Look" (`kind` "Chopped Tomato", `supply` "look only"); Work
65
65
  Station "Chopping Board" (`mode` "hold to work", `turns` "Tomato > Chopped
@@ -13,11 +13,15 @@
13
13
  "multiplayer"
14
14
  ],
15
15
  "requiresSdk": "auth-server",
16
- "version": "1.0.0",
16
+ "version": "1.0.1",
17
17
  "changelog": [
18
18
  {
19
19
  "version": "1.0.0",
20
20
  "notes": "Initial release: put down and take back one carried item, hold-to-work and timed modes that turn one kind into another with a progress bar, combining two kinds, and station events a script reacts to by the station's Name."
21
+ },
22
+ {
23
+ "version": "1.0.1",
24
+ "notes": "The AI guide names genres in its examples, not a specific game."
21
25
  }
22
26
  ],
23
27
  "origin": {