create-playcanvas 0.8.0 → 0.8.1

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/dist/index.mjs CHANGED
@@ -1,55 +1,57 @@
1
1
  #!/usr/bin/env node
2
- import{styleText as u}from"node:util";import M,{stdin as Pe,stdout as he}from"node:process";import Le from"node:readline";import"node:tty";import x from"node:fs";import y from"node:path";import{fileURLToPath as Ne}from"node:url";const De=(()=>{const e=/[\uD800-\uDBFF][\uDC00-\uDFFF]/g;return t=>{let r=0;for(e.lastIndex=0;e.test(t);)r+=1;return t.length-r}})(),We=e=>e===12288||e>=65281&&e<=65376||e>=65504&&e<=65510,Ve=e=>e===8987||e===9001||e>=12272&&e<=12287||e>=12289&&e<=12350||e>=12441&&e<=12543||e>=12549&&e<=12591||e>=12593&&e<=12686||e>=12688&&e<=12771||e>=12783&&e<=12830||e>=12832&&e<=12871||e>=12880&&e<=19903||e>=65040&&e<=65049||e>=65072&&e<=65106||e>=65108&&e<=65126||e>=65128&&e<=65131||e>=127488&&e<=127490||e>=127504&&e<=127547||e>=127552&&e<=127560||e>=131072&&e<=196605||e>=196608&&e<=262141,Ue=/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]|\u001b\]8;[^;]*;.*?(?:\u0007|\u001b\u005c)/y,Ge=/[\x00-\x08\x0A-\x1F\x7F-\x9F]{1,1000}/y,me=/(?:(?![\uFF61-\uFF9F\uFF00-\uFFEF])[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}\p{Script=Tangut}]){1,1000}/yu,Ke=/\t{1,1000}/y,de=new RegExp("[\\u{1F1E6}-\\u{1F1FF}]{2}|\\u{1F3F4}[\\u{E0061}-\\u{E007A}]{2}[\\u{E0030}-\\u{E0039}\\u{E0061}-\\u{E007A}]{1,3}\\u{E007F}|(?:\\p{Emoji}\\uFE0F\\u20E3?|\\p{Emoji_Modifier_Base}\\p{Emoji_Modifier}?|\\p{Emoji_Presentation})(?:\\u200D(?:\\p{Emoji_Modifier_Base}\\p{Emoji_Modifier}?|\\p{Emoji_Presentation}|\\p{Emoji}\\uFE0F\\u20E3?))*","yu"),He=/(?:[\x20-\x7E\xA0-\xFF](?!\uFE0F)){1,1000}/y,ze=new RegExp("\\p{M}+","gu"),Je={limit:1/0,ellipsis:""},fe=(e,t={},r={})=>{const i=t.limit??1/0,n=t.ellipsis??"",c=t?.ellipsisWidth??(n?fe(n,Je,r).width:0),a=0,o=r.controlWidth??0,s=r.tabWidth??8,l=r.emojiWidth??2,d=2,h=r.regularWidth??1,m=r.wideWidth??d,p=[[He,h],[Ue,a],[Ge,o],[Ke,s],[de,l],[me,m]];let f=0,b=0,F=e.length,C=0,O=!1,B=F,k=Math.max(0,i-c),w=0,S=0,$=0,E=0;e:for(;;){if(S>w||b>=F&&b>f){const _=e.slice(w,S)||e.slice(f,b);C=0;for(const R of _.replaceAll(ze,"")){const I=R.codePointAt(0)||0;if(We(I)?E=d:Ve(I)?E=m:E=h,$+E>k&&(B=Math.min(B,Math.max(w,f)+C)),$+E>i){O=!0;break e}C+=R.length,$+=E}w=S=0}if(b>=F)break e;for(let _=0,R=p.length;_<R;_++){const[I,g]=p[_];if(I.lastIndex=b,I.test(e)){if(C=I===me?De(e.slice(b,I.lastIndex)):I===de?1:I.lastIndex-b,E=C*g,$+E>k&&(B=Math.min(B,b+Math.floor((k-$)/g))),$+E>i){O=!0;break e}$+=E,w=f,S=b,b=f=I.lastIndex;continue e}}b+=1}return{width:O?k:$,index:O?B:F,truncated:O,ellipsed:O&&i>=c}},Ye={limit:1/0,ellipsis:"",ellipsisWidth:0},N=(e,t={})=>fe(e,Ye,t).width,z="\x1B",pe="\x9B",qe=39,ee="\x07",ge="[",Xe="]",be="m",te=`${Xe}8;;`,ye=new RegExp(`(?:\\${ge}(?<code>\\d+)m|\\${te}(?<uri>.*)${ee})`,"y"),xe=e=>{if(e>=30&&e<=37||e>=90&&e<=97)return 39;if(e>=40&&e<=47||e>=100&&e<=107)return 49;if(e===1||e===2)return 22;if(e===3)return 23;if(e===4)return 24;if(e===7)return 27;if(e===8)return 28;if(e===9)return 29;if(e===0)return 0},$e=e=>`${z}${ge}${e}${be}`,we=e=>`${z}${te}${e}${ee}`,re=(e,t,r)=>{const i=t[Symbol.iterator]();let n=!1,c=!1,a=e.at(-1),o=a===void 0?0:N(a),s=i.next(),l=i.next(),d=0;for(;!s.done;){const h=s.value,m=N(h);o+m<=r?e[e.length-1]+=h:(e.push(h),o=0),(h===z||h===pe)&&(n=!0,c=t.startsWith(te,d+1)),n?c?h===ee&&(n=!1,c=!1):h===be&&(n=!1):(o+=m,o===r&&!l.done&&(e.push(""),o=0)),s=l,l=i.next(),d+=h.length}a=e.at(-1),!o&&a!==void 0&&a.length&&e.length>1&&(e[e.length-2]+=e.pop())},Ze=e=>{const t=e.split(" ");let r=t.length;for(;r&&!N(t[r-1]);)r--;return r===t.length?e:t.slice(0,r).join(" ")+t.slice(r).join("")},Qe=(e,t,r={})=>{if(r.trim!==!1&&e.trim()==="")return"";let i="",n,c;const a=e.split(" ");let o=[""],s=0;for(let h=0;h<a.length;h++){const m=a[h];if(r.trim!==!1){const f=o.at(-1)??"",b=f.trimStart();f.length!==b.length&&(o[o.length-1]=b,s=N(b))}h!==0&&(s>=t&&(r.wordWrap===!1||r.trim===!1)&&(o.push(""),s=0),(s||r.trim===!1)&&(o[o.length-1]+=" ",s++));const p=N(m);if(r.hard&&p>t){const f=t-s,b=1+Math.floor((p-f-1)/t);Math.floor((p-1)/t)<b&&o.push(""),re(o,m,t),s=N(o.at(-1)??"");continue}if(s+p>t&&s&&p){if(r.wordWrap===!1&&s<t){re(o,m,t),s=N(o.at(-1)??"");continue}o.push(""),s=0}if(s+p>t&&r.wordWrap===!1){re(o,m,t),s=N(o.at(-1)??"");continue}o[o.length-1]+=m,s+=p}r.trim!==!1&&(o=o.map(h=>Ze(h)));const l=o.join(`
3
- `);let d=!1;for(let h=0;h<l.length;h++){const m=l[h];if(i+=m,d)d=!1;else if(d=m>="\uD800"&&m<="\uDBFF",d)continue;if(m===z||m===pe){ye.lastIndex=h+1;const f=ye.exec(l)?.groups;if(f?.code!==void 0){const b=Number.parseFloat(f.code);n=b===qe?void 0:b}else f?.uri!==void 0&&(c=f.uri.length===0?void 0:f.uri)}if(l[h+1]===`
4
- `){c&&(i+=we(""));const p=n?xe(n):void 0;n&&p&&(i+=$e(p))}else m===`
5
- `&&(n&&xe(n)&&(i+=$e(n)),c&&(i+=we(c)))}return i},et=/\r?\n/;function J(e,t,r){return String(e).normalize().split(et).map(i=>Qe(i,t,r)).join(`
6
- `)}function tt(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var se,ve;function rt(){if(ve)return se;ve=1;const e="\x1B",t=`${e}[`,r="\x07",i={to(a,o){return o?`${t}${o+1};${a+1}H`:`${t}${a+1}G`},move(a,o){let s="";return a<0?s+=`${t}${-a}D`:a>0&&(s+=`${t}${a}C`),o<0?s+=`${t}${-o}A`:o>0&&(s+=`${t}${o}B`),s},up:(a=1)=>`${t}${a}A`,down:(a=1)=>`${t}${a}B`,forward:(a=1)=>`${t}${a}C`,backward:(a=1)=>`${t}${a}D`,nextLine:(a=1)=>`${t}E`.repeat(a),prevLine:(a=1)=>`${t}F`.repeat(a),left:`${t}G`,hide:`${t}?25l`,show:`${t}?25h`,save:`${e}7`,restore:`${e}8`},n={up:(a=1)=>`${t}S`.repeat(a),down:(a=1)=>`${t}T`.repeat(a)},c={screen:`${t}2J`,up:(a=1)=>`${t}1J`.repeat(a),down:(a=1)=>`${t}J`.repeat(a),line:`${t}2K`,lineEnd:`${t}K`,lineStart:`${t}1K`,lines(a){let o="";for(let s=0;s<a;s++)o+=this.line+(s<a-1?i.up():"");return a&&(o+=i.left),o}};return se={cursor:i,scroll:n,erase:c,beep:r},se}var P=rt();function Y(e,t,r){if(!r.some(a=>!a.disabled))return e;const i=e+t,n=Math.max(r.length-1,0),c=i<0?n:i>n?0:i;return r[c]?.disabled?Y(c,t<0?-1:1,r):c}const st=["up","down","left","right","space","enter","cancel"],W={actions:new Set(st),aliases:new Map([["k","up"],["j","down"],["h","left"],["l","right"],["","cancel"],["escape","cancel"]]),withGuide:!0};function Se(e,t){if(typeof e=="string")return W.aliases.get(e)===t;for(const r of e)if(r!==void 0&&Se(r,t))return!0;return!1}function it(e,t){if(e===t)return;const r=e.split(`
2
+ import{styleText as l}from"node:util";import B,{stdin as Le,stdout as he}from"node:process";import Ne from"node:readline";import"node:tty";import $ from"node:fs";import y from"node:path";import{fileURLToPath as De}from"node:url";const We=(()=>{const e=/[\uD800-\uDBFF][\uDC00-\uDFFF]/g;return t=>{let r=0;for(e.lastIndex=0;e.test(t);)r+=1;return t.length-r}})(),Ve=e=>e===12288||e>=65281&&e<=65376||e>=65504&&e<=65510,Ue=e=>e===8987||e===9001||e>=12272&&e<=12287||e>=12289&&e<=12350||e>=12441&&e<=12543||e>=12549&&e<=12591||e>=12593&&e<=12686||e>=12688&&e<=12771||e>=12783&&e<=12830||e>=12832&&e<=12871||e>=12880&&e<=19903||e>=65040&&e<=65049||e>=65072&&e<=65106||e>=65108&&e<=65126||e>=65128&&e<=65131||e>=127488&&e<=127490||e>=127504&&e<=127547||e>=127552&&e<=127560||e>=131072&&e<=196605||e>=196608&&e<=262141,Ge=/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]|\u001b\]8;[^;]*;.*?(?:\u0007|\u001b\u005c)/y,Ke=/[\x00-\x08\x0A-\x1F\x7F-\x9F]{1,1000}/y,me=/(?:(?![\uFF61-\uFF9F\uFF00-\uFFEF])[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}\p{Script=Tangut}]){1,1000}/yu,He=/\t{1,1000}/y,de=new RegExp("[\\u{1F1E6}-\\u{1F1FF}]{2}|\\u{1F3F4}[\\u{E0061}-\\u{E007A}]{2}[\\u{E0030}-\\u{E0039}\\u{E0061}-\\u{E007A}]{1,3}\\u{E007F}|(?:\\p{Emoji}\\uFE0F\\u20E3?|\\p{Emoji_Modifier_Base}\\p{Emoji_Modifier}?|\\p{Emoji_Presentation})(?:\\u200D(?:\\p{Emoji_Modifier_Base}\\p{Emoji_Modifier}?|\\p{Emoji_Presentation}|\\p{Emoji}\\uFE0F\\u20E3?))*","yu"),ze=/(?:[\x20-\x7E\xA0-\xFF](?!\uFE0F)){1,1000}/y,Je=new RegExp("\\p{M}+","gu"),Ye={limit:1/0,ellipsis:""},fe=(e,t={},r={})=>{const i=t.limit??1/0,n=t.ellipsis??"",c=t?.ellipsisWidth??(n?fe(n,Ye,r).width:0),a=0,o=r.controlWidth??0,s=r.tabWidth??8,u=r.emojiWidth??2,d=2,h=r.regularWidth??1,m=r.wideWidth??d,p=[[ze,h],[Ge,a],[Ke,o],[He,s],[de,u],[me,m]];let f=0,b=0,F=e.length,T=0,O=!1,M=F,k=Math.max(0,i-c),w=0,S=0,x=0,E=0;e:for(;;){if(S>w||b>=F&&b>f){const _=e.slice(w,S)||e.slice(f,b);T=0;for(const R of _.replaceAll(Je,"")){const I=R.codePointAt(0)||0;if(Ve(I)?E=d:Ue(I)?E=m:E=h,x+E>k&&(M=Math.min(M,Math.max(w,f)+T)),x+E>i){O=!0;break e}T+=R.length,x+=E}w=S=0}if(b>=F)break e;for(let _=0,R=p.length;_<R;_++){const[I,g]=p[_];if(I.lastIndex=b,I.test(e)){if(T=I===me?We(e.slice(b,I.lastIndex)):I===de?1:I.lastIndex-b,E=T*g,x+E>k&&(M=Math.min(M,b+Math.floor((k-x)/g))),x+E>i){O=!0;break e}x+=E,w=f,S=b,b=f=I.lastIndex;continue e}}b+=1}return{width:O?k:x,index:O?M:F,truncated:O,ellipsed:O&&i>=c}},qe={limit:1/0,ellipsis:"",ellipsisWidth:0},N=(e,t={})=>fe(e,qe,t).width,z="\x1B",pe="\x9B",Xe=39,te="\x07",ge="[",Ze="]",be="m",re=`${Ze}8;;`,ye=new RegExp(`(?:\\${ge}(?<code>\\d+)m|\\${re}(?<uri>.*)${te})`,"y"),$e=e=>{if(e>=30&&e<=37||e>=90&&e<=97)return 39;if(e>=40&&e<=47||e>=100&&e<=107)return 49;if(e===1||e===2)return 22;if(e===3)return 23;if(e===4)return 24;if(e===7)return 27;if(e===8)return 28;if(e===9)return 29;if(e===0)return 0},xe=e=>`${z}${ge}${e}${be}`,we=e=>`${z}${re}${e}${te}`,se=(e,t,r)=>{const i=t[Symbol.iterator]();let n=!1,c=!1,a=e.at(-1),o=a===void 0?0:N(a),s=i.next(),u=i.next(),d=0;for(;!s.done;){const h=s.value,m=N(h);o+m<=r?e[e.length-1]+=h:(e.push(h),o=0),(h===z||h===pe)&&(n=!0,c=t.startsWith(re,d+1)),n?c?h===te&&(n=!1,c=!1):h===be&&(n=!1):(o+=m,o===r&&!u.done&&(e.push(""),o=0)),s=u,u=i.next(),d+=h.length}a=e.at(-1),!o&&a!==void 0&&a.length&&e.length>1&&(e[e.length-2]+=e.pop())},Qe=e=>{const t=e.split(" ");let r=t.length;for(;r&&!N(t[r-1]);)r--;return r===t.length?e:t.slice(0,r).join(" ")+t.slice(r).join("")},et=(e,t,r={})=>{if(r.trim!==!1&&e.trim()==="")return"";let i="",n,c;const a=e.split(" ");let o=[""],s=0;for(let h=0;h<a.length;h++){const m=a[h];if(r.trim!==!1){const f=o.at(-1)??"",b=f.trimStart();f.length!==b.length&&(o[o.length-1]=b,s=N(b))}h!==0&&(s>=t&&(r.wordWrap===!1||r.trim===!1)&&(o.push(""),s=0),(s||r.trim===!1)&&(o[o.length-1]+=" ",s++));const p=N(m);if(r.hard&&p>t){const f=t-s,b=1+Math.floor((p-f-1)/t);Math.floor((p-1)/t)<b&&o.push(""),se(o,m,t),s=N(o.at(-1)??"");continue}if(s+p>t&&s&&p){if(r.wordWrap===!1&&s<t){se(o,m,t),s=N(o.at(-1)??"");continue}o.push(""),s=0}if(s+p>t&&r.wordWrap===!1){se(o,m,t),s=N(o.at(-1)??"");continue}o[o.length-1]+=m,s+=p}r.trim!==!1&&(o=o.map(h=>Qe(h)));const u=o.join(`
3
+ `);let d=!1;for(let h=0;h<u.length;h++){const m=u[h];if(i+=m,d)d=!1;else if(d=m>="\uD800"&&m<="\uDBFF",d)continue;if(m===z||m===pe){ye.lastIndex=h+1;const f=ye.exec(u)?.groups;if(f?.code!==void 0){const b=Number.parseFloat(f.code);n=b===Xe?void 0:b}else f?.uri!==void 0&&(c=f.uri.length===0?void 0:f.uri)}if(u[h+1]===`
4
+ `){c&&(i+=we(""));const p=n?$e(n):void 0;n&&p&&(i+=xe(p))}else m===`
5
+ `&&(n&&$e(n)&&(i+=xe(n)),c&&(i+=we(c)))}return i},tt=/\r?\n/;function J(e,t,r){return String(e).normalize().split(tt).map(i=>et(i,t,r)).join(`
6
+ `)}function rt(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var ie,ve;function st(){if(ve)return ie;ve=1;const e="\x1B",t=`${e}[`,r="\x07",i={to(a,o){return o?`${t}${o+1};${a+1}H`:`${t}${a+1}G`},move(a,o){let s="";return a<0?s+=`${t}${-a}D`:a>0&&(s+=`${t}${a}C`),o<0?s+=`${t}${-o}A`:o>0&&(s+=`${t}${o}B`),s},up:(a=1)=>`${t}${a}A`,down:(a=1)=>`${t}${a}B`,forward:(a=1)=>`${t}${a}C`,backward:(a=1)=>`${t}${a}D`,nextLine:(a=1)=>`${t}E`.repeat(a),prevLine:(a=1)=>`${t}F`.repeat(a),left:`${t}G`,hide:`${t}?25l`,show:`${t}?25h`,save:`${e}7`,restore:`${e}8`},n={up:(a=1)=>`${t}S`.repeat(a),down:(a=1)=>`${t}T`.repeat(a)},c={screen:`${t}2J`,up:(a=1)=>`${t}1J`.repeat(a),down:(a=1)=>`${t}J`.repeat(a),line:`${t}2K`,lineEnd:`${t}K`,lineStart:`${t}1K`,lines(a){let o="";for(let s=0;s<a;s++)o+=this.line+(s<a-1?i.up():"");return a&&(o+=i.left),o}};return ie={cursor:i,scroll:n,erase:c,beep:r},ie}var P=st();function Y(e,t,r){if(!r.some(a=>!a.disabled))return e;const i=e+t,n=Math.max(r.length-1,0),c=i<0?n:i>n?0:i;return r[c]?.disabled?Y(c,t<0?-1:1,r):c}const it=["up","down","left","right","space","enter","cancel"],W={actions:new Set(it),aliases:new Map([["k","up"],["j","down"],["h","left"],["l","right"],["","cancel"],["escape","cancel"]]),withGuide:!0};function nt(e){if(e!==void 0)return e;const t=process.env.ACCESSIBLE;return t!==void 0&&t!==""&&t!=="0"&&t!=="false"}function Se(e,t){if(typeof e=="string")return W.aliases.get(e)===t;for(const r of e)if(r!==void 0&&Se(r,t))return!0;return!1}function at(e,t){if(e===t)return;const r=e.split(`
7
7
  `),i=t.split(`
8
- `),n=Math.max(r.length,i.length),c=[];for(let a=0;a<n;a++)r[a]!==i[a]&&c.push(a);return{lines:c,numLinesBefore:r.length,numLinesAfter:i.length,numLines:n}}globalThis.process.platform.startsWith("win");const ie=Symbol("clack:cancel");function K(e){return e===ie}function q(e,t){const r=e;r.isTTY&&r.setRawMode(t)}const Ee=e=>"columns"in e&&typeof e.columns=="number"?e.columns:80,_e=e=>"rows"in e&&typeof e.rows=="number"?e.rows:20;function ne(e,t,r,i=r,n=r,c){const a=Ee(e??he);return J(t,a-r.length,{hard:!0,trim:!1}).split(`
9
- `).map((o,s,l)=>{const d=o;return s===0?`${i}${d}`:s===l.length-1?`${n}${d}`:`${r}${d}`}).join(`
10
- `)}function nt(e,t){if("~standard"in e){const r=e["~standard"].validate(t);if(r instanceof Promise)throw new TypeError("Schema validation must be synchronous. Update `validate()` and remove any asynchronous logic.");return r.issues?.at(0)?.message}return e(t)}class Ie{input;output;_abortSignal;rl;opts;_render;_track=!1;_prevFrame="";_subscribers=new Map;_cursor=0;state="initial";error="";value;userInput="";constructor(t,r=!0){const{input:i=Pe,output:n=he,render:c,signal:a,...o}=t;this.opts=o,this.onKeypress=this.onKeypress.bind(this),this.close=this.close.bind(this),this.render=this.render.bind(this),this._render=c.bind(this),this._track=r,this._abortSignal=a,this.input=i,this.output=n}unsubscribe(){this._subscribers.clear()}setSubscriber(t,r){const i=this._subscribers.get(t)??[];i.push(r),this._subscribers.set(t,i)}on(t,r){this.setSubscriber(t,{cb:r})}once(t,r){this.setSubscriber(t,{cb:r,once:!0})}emit(t,...r){const i=this._subscribers.get(t)??[],n=[];for(const c of i)c.cb(...r),c.once&&n.push(()=>i.splice(i.indexOf(c),1));for(const c of n)c()}prompt(){return new Promise(t=>{if(this._abortSignal){if(this._abortSignal.aborted)return this.state="cancel",this.close(),t(ie);this._abortSignal.addEventListener("abort",()=>{this.state="cancel",this.close()},{once:!0})}this.rl=Le.createInterface({input:this.input,tabSize:2,prompt:"",escapeCodeTimeout:50,terminal:!0}),this.rl.prompt(),this.opts.initialUserInput!==void 0&&this._setUserInput(this.opts.initialUserInput,!0),this.input.on("keypress",this.onKeypress),q(this.input,!0),this.output.on("resize",this.render),this.render(),this.once("submit",()=>{this.output.write(P.cursor.show),this.output.off("resize",this.render),q(this.input,!1),t(this.value)}),this.once("cancel",()=>{this.output.write(P.cursor.show),this.output.off("resize",this.render),q(this.input,!1),t(ie)})})}_isActionKey(t,r){return t===" "}_shouldSubmit(t,r){return!0}_setValue(t){this.value=t,this.emit("value",this.value)}_setUserInput(t,r){this.userInput=t??"",this.emit("userInput",this.userInput),r&&this._track&&this.rl&&(this.rl.write(this.userInput),this._cursor=this.rl.cursor)}_clearUserInput(){this.rl?.write(null,{ctrl:!0,name:"u"}),this._setUserInput("")}onKeypress(t,r){if(this._track&&r.name!=="return"&&(r.name&&this._isActionKey(t,r)&&this.rl?.write(null,{ctrl:!0,name:"h"}),this._cursor=this.rl?.cursor??0,this._setUserInput(this.rl?.line)),this.state==="error"&&(this.state="active"),r?.name&&(!this._track&&W.aliases.has(r.name)&&this.emit("cursor",W.aliases.get(r.name)),W.actions.has(r.name)&&this.emit("cursor",r.name)),t&&(t.toLowerCase()==="y"||t.toLowerCase()==="n")&&this.emit("confirm",t.toLowerCase()==="y"),this.emit("key",t,r),r?.name==="return"&&this._shouldSubmit(t,r)){if(this.opts.validate){const i=nt(this.opts.validate,this.value);i&&(this.error=i instanceof Error?i.message:i,this.state="error",this.rl?.write(this.userInput))}this.state!=="error"&&(this.state="submit")}Se([t,r?.name,r?.sequence],"cancel")&&(this.state="cancel"),(this.state==="submit"||this.state==="cancel")&&this.emit("finalize"),this.render(),(this.state==="submit"||this.state==="cancel")&&this.close()}close(){this.input.unpipe(),this.input.removeListener("keypress",this.onKeypress),this.output.write(`
8
+ `),n=Math.max(r.length,i.length),c=[];for(let a=0;a<n;a++)r[a]!==i[a]&&c.push(a);return{lines:c,numLinesBefore:r.length,numLinesAfter:i.length,numLines:n}}globalThis.process.platform.startsWith("win");const ne=Symbol("clack:cancel");function K(e){return e===ne}function q(e,t){const r=e;r.isTTY&&r.setRawMode(t)}const Ee=e=>"columns"in e&&typeof e.columns=="number"?e.columns:80,_e=e=>"rows"in e&&typeof e.rows=="number"?e.rows:20;function ae(e,t,r,i=r,n=r,c){const a=Ee(e??he);return J(t,a-r.length,{hard:!0,trim:!1}).split(`
9
+ `).map((o,s,u)=>{const d=o;return s===0?`${i}${d}`:s===u.length-1?`${n}${d}`:`${r}${d}`}).join(`
10
+ `)}function ot(e,t){if("~standard"in e){const r=e["~standard"].validate(t);return r instanceof Promise?r.then(i=>i.issues?.at(0)?.message):r.issues?.at(0)?.message}return e(t)}class Ie{input;output;_abortSignal;rl;opts;_render;_track=!1;_prevFrame="";_subscribers=new Map;_cursor=0;state="initial";error="";value;userInput="";get accessible(){return nt(this.opts.accessible)}constructor(t,r=!0){const{input:i=Le,output:n=he,render:c,signal:a,...o}=t;this.opts=o,this.onKeypress=this.onKeypress.bind(this),this.close=this.close.bind(this),this.render=this.render.bind(this),this._render=c.bind(this),this._track=r,this._abortSignal=a,this.input=i,this.output=n}unsubscribe(){this._subscribers.clear()}setSubscriber(t,r){const i=this._subscribers.get(t)??[];i.push(r),this._subscribers.set(t,i)}on(t,r){this.setSubscriber(t,{cb:r})}once(t,r){this.setSubscriber(t,{cb:r,once:!0})}emit(t,...r){const i=this._subscribers.get(t)??[],n=[];for(const c of i)c.cb(...r),c.once&&n.push(()=>i.splice(i.indexOf(c),1));for(const c of n)c()}prompt(){return new Promise(t=>{if(this._abortSignal){if(this._abortSignal.aborted)return this.state="cancel",this.close(),t(ne);this._abortSignal.addEventListener("abort",()=>{this.state="cancel",this.close()},{once:!0})}this.rl=Ne.createInterface({input:this.input,tabSize:2,prompt:"",escapeCodeTimeout:50,terminal:!0}),this.rl.prompt(),this.opts.initialUserInput!==void 0&&this._setUserInput(this.opts.initialUserInput,!0),this.input.on("keypress",this.onKeypress),q(this.input,!0),this.output.on("resize",this.render),this.render(),this.once("submit",()=>{this.output.write(P.cursor.show),this.output.off("resize",this.render),q(this.input,!1),t(this.value)}),this.once("cancel",()=>{this.output.write(P.cursor.show),this.output.off("resize",this.render),q(this.input,!1),t(ne)})})}_isActionKey(t,r){return t===" "}_shouldSubmit(t,r){return!0}_setValue(t){this.value=t,this.emit("value",this.value)}_setUserInput(t,r){this.userInput=t??"",this.emit("userInput",this.userInput),r&&this._track&&this.rl&&(this.rl.write(this.userInput),this._cursor=this.rl.cursor)}_clearUserInput(){this.rl?.write(null,{ctrl:!0,name:"u"}),this._setUserInput("")}async onKeypress(t,r){if(this.state!=="validating"){if(this._track&&r.name!=="return"&&(r.name&&this._isActionKey(t,r)&&this.rl?.write(null,{ctrl:!0,name:"h"}),this._cursor=this.rl?.cursor??0,this._setUserInput(this.rl?.line)),this.state==="error"&&(this.state="active"),r?.name&&(!this._track&&W.aliases.has(r.name)&&this.emit("cursor",W.aliases.get(r.name)),W.actions.has(r.name)&&this.emit("cursor",r.name)),t&&(t.toLowerCase()==="y"||t.toLowerCase()==="n")&&this.emit("confirm",t.toLowerCase()==="y"),this.emit("key",t,r),r?.name==="return"&&this._shouldSubmit(t,r)){if(this.opts.validate){const i=ot(this.opts.validate,this.value);let n;i instanceof Promise?(this.state="validating",this.render(),n=await i):n=i,n&&(this.error=n instanceof Error?n.message:n,this.state="error",this.rl?.write(this.userInput))}this.state!=="error"&&(this.state="submit")}Se([t,r?.name,r?.sequence],"cancel")&&(this.state="cancel"),(this.state==="submit"||this.state==="cancel")&&this.emit("finalize"),this.render(),(this.state==="submit"||this.state==="cancel")&&this.close()}}close(){this.input.unpipe(),this.input.removeListener("keypress",this.onKeypress),this.output.write(`
11
11
  `),q(this.input,!1),this.rl?.close(),this.rl=void 0,this.emit(`${this.state}`,this.value),this.unsubscribe()}restoreCursor(){const t=J(this._prevFrame,process.stdout.columns,{hard:!0,trim:!1}).split(`
12
- `).length-1;this.output.write(P.cursor.move(-999,t*-1))}render(){const t=J(this._render(this)??"",process.stdout.columns,{hard:!0,trim:!1});if(t!==this._prevFrame){if(this.state==="initial")this.output.write(P.cursor.hide);else{const r=it(this._prevFrame,t),i=_e(this.output);if(this.restoreCursor(),r){const n=Math.max(0,r.numLinesAfter-i),c=Math.max(0,r.numLinesBefore-i);let a=r.lines.find(o=>o>=n);if(a===void 0){this._prevFrame=t;return}if(r.lines.length===1){this.output.write(P.cursor.move(0,a-c)),this.output.write(P.erase.lines(1));const o=t.split(`
12
+ `).length-1;this.output.write(P.cursor.move(-999,t*-1))}render(){const t=J(this._render(this)??"",process.stdout.columns,{hard:!0,trim:!1});if(t!==this._prevFrame){if(this.state==="initial")this.output.write(P.cursor.hide);else{const r=at(this._prevFrame,t),i=_e(this.output);if(this.restoreCursor(),r){const n=Math.max(0,r.numLinesAfter-i),c=Math.max(0,r.numLinesBefore-i);let a=r.lines.find(o=>o>=n);if(a===void 0){this._prevFrame=t;return}if(r.lines.length===1){this.output.write(P.cursor.move(0,a-c)),this.output.write(P.erase.lines(1));const o=t.split(`
13
13
  `);this.output.write(o[a]),this._prevFrame=t,this.output.write(P.cursor.move(0,o.length-a-1));return}else if(r.lines.length>1){if(n<c)a=n;else{const s=a-c;s>0&&this.output.write(P.cursor.move(0,s))}this.output.write(P.erase.down());const o=t.split(`
14
14
  `).slice(a);this.output.write(o.join(`
15
- `)),this._prevFrame=t;return}}this.output.write(P.erase.down())}this.output.write(t),this.state==="initial"&&(this.state="active"),this._prevFrame=t}}}let at=class extends Ie{options;cursor=0;get _selectedValue(){return this.options[this.cursor]}changeValue(){const t=this._selectedValue;this.value=t===void 0?void 0:t.value}constructor(t){super(t,!1),this.options=t.options;const r=this.options.findIndex(({value:n})=>n===t.initialValue),i=r===-1?0:r;this.cursor=this.options[i]?.disabled?Y(i,1,this.options):i,this.changeValue(),this.on("cursor",n=>{switch(n){case"left":case"up":this.cursor=Y(this.cursor,-1,this.options);break;case"down":case"right":this.cursor=Y(this.cursor,1,this.options);break}this.changeValue()})}};class ot extends Ie{get userInputWithCursor(){if(this.state==="submit")return this.userInput;const t=this.userInput;if(this.cursor>=t.length)return`${this.userInput}\u2588`;const r=t.slice(0,this.cursor),i=t.slice(this.cursor,this.cursor+1),n=t.slice(this.cursor+1);return`${r}${u("inverse",i)}${n}`}get cursor(){return this._cursor}constructor(t){super({...t,initialUserInput:t.initialUserInput??t.initialValue}),this.on("userInput",r=>{this._setValue(r)}),this.on("finalize",()=>{this.value||(this.value=t.defaultValue),this.value===void 0&&(this.value="")})}}function ct(){return M.platform!=="win32"?M.env.TERM!=="linux":!!M.env.CI||!!M.env.WT_SESSION||!!M.env.TERMINUS_SUBLIME||M.env.ConEmuTask==="{cmd::Cmder}"||M.env.TERM_PROGRAM==="Terminus-Sublime"||M.env.TERM_PROGRAM==="vscode"||M.env.TERM==="xterm-256color"||M.env.TERM==="alacritty"||M.env.TERMINAL_EMULATOR==="JetBrains-JediTerm"}const lt=ct(),j=(e,t)=>lt?e:t,ut=j("\u25C6","*"),ht=j("\u25A0","x"),mt=j("\u25B2","x"),Fe=j("\u25C7","o"),v=j("\u2502","|"),U=j("\u2514","\u2014"),dt=j("\u25CF",">"),Te=j("\u25CB"," "),ft=j("\u25CF","\u2022"),pt=j("\u25C6","*"),gt=j("\u25B2","!"),bt=j("\u25A0","x"),Ce=e=>{switch(e){case"initial":case"active":return u("cyan",ut);case"cancel":return u("red",ht);case"error":return u("yellow",mt);case"submit":return u("green",Fe)}},yt=e=>{switch(e){case"initial":case"active":return u("cyan",v);case"cancel":return u("red",v);case"error":return u("yellow",v);case"submit":return u("green",v)}};function xt(e,t){const r=[`${t?`${u("cyan",v)} `:""}${e.join(" \u2022 ")}`];return t&&r.push(u("cyan",U)),r}const Re=(e,t,r,i,n,c=!1)=>{let a=t,o=0;if(c)for(let s=i-1;s>=r;s--){const l=e[s];if(l&&(a-=l.length),o++,a<=n)break}else for(let s=r;s<i;s++){const l=e[s];if(l&&(a-=l.length),o++,a<=n)break}return{lineCount:a,removals:o}},$t=({cursor:e,options:t,style:r,output:i=process.stdout,maxItems:n=Number.POSITIVE_INFINITY,columnPadding:c=0,rowPadding:a=4})=>{const o=Ee(i)-c,s=_e(i),l=u("dim","..."),d=Math.max(s-a,0),h=Math.max(Math.min(n,d),5);let m=0;e>=h-3&&(m=Math.max(Math.min(e-h+3,t.length-h),0));let p=h<t.length&&m>0,f=h<t.length&&m+h<t.length;const b=Math.min(m+h,t.length),F=[];let C=0;p&&C++,f&&C++;const O=m+(p?1:0),B=b-(f?1:0);for(let w=O;w<B;w++){const S=t[w],$=S?r(S,w===e):"",E=J($,o,{hard:!0,trim:!1}).split(`
16
- `);F.push(E),C+=E.length}if(C>d){let w=0,S=0,$=C;const E=e-O;let _=d;const R=()=>Re(F,$,0,E,_),I=()=>Re(F,$,E+1,F.length,_,!0);p?({lineCount:$,removals:w}=R(),$>_&&(f||(_-=1),{lineCount:$,removals:S}=I())):(f||(_-=1),{lineCount:$,removals:S}=I(),$>_&&(_-=1,{lineCount:$,removals:w}=R())),w>0&&(p=!0,F.splice(0,w)),S>0&&(f=!0,F.splice(F.length-S,S))}const k=[];p&&k.push(l);for(const w of F)for(const S of w)k.push(S);return f&&k.push(l),k};`${u("dim","\u2191/\u2193")}`,`${u("dim","Space:")}`,`${u("dim","Enter:")}`;const D={message:(e=[],{symbol:t=u("gray",v),secondarySymbol:r=u("gray",v),output:i=process.stdout,spacing:n=1,withGuide:c}={})=>{const a=[],o=c??W.withGuide,s=o?r:"",l=o?`${t} `:"",d=o?`${r} `:"";for(let m=0;m<n;m++)a.push(s);const h=Array.isArray(e)?e:e.split(`
17
- `);if(h.length>0){const[m,...p]=h;m.length>0?a.push(`${l}${m}`):a.push(o?t:"");for(const f of p)f.length>0?a.push(`${d}${f}`):a.push(o?r:"")}i.write(`${a.join(`
15
+ `)),this._prevFrame=t;return}}this.output.write(P.erase.down())}this.output.write(t),this.state==="initial"&&(this.state="active"),this._prevFrame=t}}}let ct=class extends Ie{options;cursor=0;get _selectedValue(){return this.options[this.cursor]}changeValue(){const t=this._selectedValue;this.value=t===void 0?void 0:t.value}constructor(t){super(t,!1),this.options=t.options;const r=this.options.findIndex(({value:n})=>n===t.initialValue),i=r===-1?0:r;this.cursor=this.options[i]?.disabled?Y(i,1,this.options):i,this.changeValue(),this.on("cursor",n=>{switch(n){case"left":case"up":this.cursor=Y(this.cursor,-1,this.options);break;case"down":case"right":this.cursor=Y(this.cursor,1,this.options);break}this.changeValue()})}};class lt extends Ie{get userInputWithCursor(){if(this.state==="submit")return this.userInput;const t=this.userInput;if(this.cursor>=t.length)return`${this.userInput}\u2588`;const r=t.slice(0,this.cursor),i=t.slice(this.cursor,this.cursor+1),n=t.slice(this.cursor+1);return`${r}${l("inverse",i)}${n}`}get cursor(){return this._cursor}constructor(t){super({...t,initialUserInput:t.initialUserInput??t.initialValue}),this.on("userInput",r=>{this._setValue(r)}),this.on("finalize",()=>{this.value||(this.value=t.defaultValue),this.value===void 0&&(this.value="")})}}function ut(){return B.platform!=="win32"?B.env.TERM!=="linux":!!B.env.CI||!!B.env.WT_SESSION||!!B.env.TERMINUS_SUBLIME||B.env.ConEmuTask==="{cmd::Cmder}"||B.env.TERM_PROGRAM==="Terminus-Sublime"||B.env.TERM_PROGRAM==="vscode"||B.env.TERM==="xterm-256color"||B.env.TERM==="alacritty"||B.env.TERMINAL_EMULATOR==="JetBrains-JediTerm"}const ht=ut(),j=(e,t)=>ht?e:t,Fe=j("\u25C6","*"),mt=j("\u25A0","x"),dt=j("\u25B2","x"),Ce=j("\u25C7","o"),v=j("\u2502","|"),V=j("\u2514","\u2014"),ft=j("\u25CF",">"),Te=j("\u25CB"," "),pt=j("\u25CF","\u2022"),gt=j("\u25C6","*"),bt=j("\u25B2","!"),yt=j("\u25A0","x"),Re=e=>{switch(e){case"initial":case"active":return l("cyan",Fe);case"cancel":return l("red",mt);case"error":return l("yellow",dt);case"submit":return l("green",Ce);case"validating":return l("dim",Fe)}},$t=e=>{switch(e){case"initial":case"active":return l("cyan",v);case"cancel":return l("red",v);case"error":return l("yellow",v);case"submit":return l("green",v)}};function xt(e,t){const r=[`${t?`${l("cyan",v)} `:""}${e.join(" \u2022 ")}`];return t&&r.push(l("cyan",V)),r}const Ae=(e,t,r,i,n,c=!1)=>{let a=t,o=0;if(c)for(let s=i-1;s>=r;s--){const u=e[s];if(u&&(a-=u.length),o++,a<=n)break}else for(let s=r;s<i;s++){const u=e[s];if(u&&(a-=u.length),o++,a<=n)break}return{lineCount:a,removals:o}},wt=({cursor:e,options:t,style:r,output:i=process.stdout,maxItems:n=Number.POSITIVE_INFINITY,columnPadding:c=0,rowPadding:a=4})=>{const o=Ee(i)-c,s=_e(i),u=l("dim","..."),d=Math.max(s-a,0),h=Math.max(Math.min(n,d),5);let m=0;e>=h-3&&(m=Math.max(Math.min(e-h+3,t.length-h),0));let p=h<t.length&&m>0,f=h<t.length&&m+h<t.length;const b=Math.min(m+h,t.length),F=[];let T=0;p&&T++,f&&T++;const O=m+(p?1:0),M=b-(f?1:0);for(let w=O;w<M;w++){const S=t[w],x=S?r(S,w===e):"",E=J(x,o,{hard:!0,trim:!1}).split(`
16
+ `);F.push(E),T+=E.length}if(T>d){let w=0,S=0,x=T;const E=e-O;let _=d;const R=()=>Ae(F,x,0,E,_),I=()=>Ae(F,x,E+1,F.length,_,!0);p?({lineCount:x,removals:w}=R(),x>_&&(f||(_-=1),{lineCount:x,removals:S}=I())):(f||(_-=1),{lineCount:x,removals:S}=I(),x>_&&(_-=1,{lineCount:x,removals:w}=R())),w>0&&(p=!0,F.splice(0,w)),S>0&&(f=!0,F.splice(F.length-S,S))}const k=[];p&&k.push(u);for(const w of F)for(const S of w)k.push(S);return f&&k.push(u),k};`${l("dim","\u2191/\u2193")}`,`${l("dim","Space:")}`,`${l("dim","Enter:")}`;const D={message:(e=[],{symbol:t=l("gray",v),secondarySymbol:r=l("gray",v),output:i=process.stdout,spacing:n=1,withGuide:c}={})=>{const a=[],o=c??W.withGuide,s=o?r:"",u=o?`${t} `:"",d=o?`${r} `:"";for(let m=0;m<n;m++)a.push(s);const h=Array.isArray(e)?e:e.split(`
17
+ `);if(h.length>0){const[m,...p]=h;m.length>0?a.push(`${u}${m}`):a.push(o?t:"");for(const f of p)f.length>0?a.push(`${d}${f}`):a.push(o?r:"")}i.write(`${a.join(`
18
18
  `)}
19
- `)},info:(e,t)=>{D.message(e,{...t,symbol:u("blue",ft)})},success:(e,t)=>{D.message(e,{...t,symbol:u("green",pt)})},step:(e,t)=>{D.message(e,{...t,symbol:u("green",Fe)})},warn:(e,t)=>{D.message(e,{...t,symbol:u("yellow",gt)})},warning:(e,t)=>{D.warn(e,t)},error:(e,t)=>{D.message(e,{...t,symbol:u("red",bt)})}},wt=(e="",t)=>{const r=process.stdout,i=`${u("gray",U)} `;r.write(`${i}${u("red",e)}
19
+ `)},info:(e,t)=>{D.message(e,{...t,symbol:l("blue",pt)})},success:(e,t)=>{D.message(e,{...t,symbol:l("green",gt)})},step:(e,t)=>{D.message(e,{...t,symbol:l("green",Ce)})},warn:(e,t)=>{D.message(e,{...t,symbol:l("yellow",bt)})},warning:(e,t)=>{D.warn(e,t)},error:(e,t)=>{D.message(e,{...t,symbol:l("red",yt)})}},vt=(e="",t)=>{const r=process.stdout,i=`${l("gray",V)} `;r.write(`${i}${l("red",e)}
20
20
 
21
- `)},vt=(e="",t)=>{const r=process.stdout,i=`${u("gray",v)}
22
- ${u("gray",U)} `;r.write(`${i}${e}
21
+ `)},St=(e="",t)=>{const r=process.stdout,i=`${l("gray",v)}
22
+ ${l("gray",V)} `;r.write(`${i}${e}
23
23
 
24
- `)},St=[`${u("dim","\u2191/\u2193")} to navigate`,`${u("dim","Enter:")} confirm`],X=(e,t)=>e.includes(`
24
+ `)},Et=[`${l("dim","\u2191/\u2193")} to navigate`,`${l("dim","Enter:")} confirm`],X=(e,t)=>e.includes(`
25
25
  `)?e.split(`
26
26
  `).map(r=>t(r)).join(`
27
- `):t(e),ae=e=>{const t=(i,n)=>{if(i===void 0)return"";const c=i.label??String(i.value);switch(n){case"disabled":return`${u("gray",Te)} ${X(c,a=>u("gray",a))}${i.hint?` ${u("dim",`(${i.hint??"disabled"})`)}`:""}`;case"selected":return`${X(c,a=>u("dim",a))}`;case"active":return`${u("green",dt)} ${c}${i.hint?` ${u("dim",`(${i.hint})`)}`:""}`;case"cancelled":return`${X(c,a=>u(["strikethrough","dim"],a))}`;default:return`${u("dim",Te)} ${X(c,a=>u("dim",a))}`}},r=e.showInstructions??!0;return new at({options:e.options,signal:e.signal,input:e.input,output:e.output,initialValue:e.initialValue,render(){const i=e.withGuide??W.withGuide,n=`${Ce(this.state)} `,c=`${yt(this.state)} `,a=ne(e.output,e.message,c,n),o=`${i?`${u("gray",v)}
27
+ `):t(e),oe=e=>{const t=(i,n)=>{if(i===void 0)return"";const c=i.label??String(i.value);switch(n){case"disabled":return`${l("gray",Te)} ${X(c,a=>l("gray",a))}${i.hint?` ${l("dim",`(${i.hint??"disabled"})`)}`:""}`;case"selected":return`${X(c,a=>l("dim",a))}`;case"active":return`${l("green",ft)} ${c}${i.hint?` ${l("dim",`(${i.hint})`)}`:""}`;case"cancelled":return`${X(c,a=>l(["strikethrough","dim"],a))}`;default:return`${l("dim",Te)} ${X(c,a=>l("dim",a))}`}},r=e.showInstructions??!0;return new ct({options:e.options,signal:e.signal,input:e.input,output:e.output,initialValue:e.initialValue,render(){const i=e.withGuide??W.withGuide,n=`${Re(this.state)} `,c=`${$t(this.state)} `,a=ae(e.output,e.message,c,n),o=`${i?`${l("gray",v)}
28
28
  `:""}${a}
29
- `;switch(this.state){case"submit":{const s=i?`${u("gray",v)} `:"",l=ne(e.output,t(this.options[this.cursor],"selected"),s);return`${o}${l}`}case"cancel":{const s=i?`${u("gray",v)} `:"",l=ne(e.output,t(this.options[this.cursor],"cancelled"),s);return`${o}${l}${i?`
30
- ${u("gray",v)}`:""}`}default:{const s=i?`${u("cyan",v)} `:"",l=o.split(`
31
- `).length,d=r?xt(St,i):i?[u("cyan",U)]:[],h=d.join(`
32
- `),m=d.length+1;return`${o}${s}${$t({output:e.output,cursor:this.cursor,options:this.options,maxItems:e.maxItems,columnPadding:s.length,rowPadding:l+m,style:(p,f)=>t(p,p.disabled?"disabled":f?"active":"inactive")}).join(`
29
+ `;switch(this.state){case"submit":{const s=i?`${l("gray",v)} `:"",u=ae(e.output,t(this.options[this.cursor],"selected"),s);return`${o}${u}`}case"cancel":{const s=i?`${l("gray",v)} `:"",u=ae(e.output,t(this.options[this.cursor],"cancelled"),s);return`${o}${u}${i?`
30
+ ${l("gray",v)}`:""}`}default:{const s=i?`${l("cyan",v)} `:"",u=o.split(`
31
+ `).length,d=r?xt(Et,i):i?[l("cyan",V)]:[],h=d.join(`
32
+ `),m=d.length+1;return`${o}${s}${wt({output:e.output,cursor:this.cursor,options:this.options,maxItems:e.maxItems,columnPadding:s.length,rowPadding:u+m,style:(p,f)=>t(p,p.disabled?"disabled":f?"active":"inactive")}).join(`
33
33
  ${s}`)}
34
34
  ${h}
35
- `}}}}).prompt()};`${u("gray",v)}`;const Ae=e=>new ot({validate:e.validate,placeholder:e.placeholder,defaultValue:e.defaultValue,initialValue:e.initialValue,output:e.output,signal:e.signal,input:e.input,render(){const t=e?.withGuide??W.withGuide,r=`${`${t?`${u("gray",v)}
36
- `:""}${Ce(this.state)} `}${e.message}
37
- `,i=e.placeholder&&e.placeholder.length>0?u("inverse",e.placeholder[0])+u("dim",e.placeholder.slice(1)):u(["inverse","hidden"],"_"),n=this.userInput?this.userInputWithCursor:i,c=this.value??"";switch(this.state){case"error":{const a=this.error?` ${u("yellow",this.error)}`:"",o=t?`${u("yellow",v)} `:"",s=t?u("yellow",U):"";return`${r.trim()}
35
+ `}}}}).prompt()};`${l("gray",v)}`;const ke=e=>new lt({validate:e.validate,placeholder:e.placeholder,defaultValue:e.defaultValue,initialValue:e.initialValue,output:e.output,signal:e.signal,input:e.input,render(){const t=e?.withGuide??W.withGuide,r=`${`${t?`${l("gray",v)}
36
+ `:""}${Re(this.state)} `}${e.message}
37
+ `,i=e.placeholder&&e.placeholder.length>0?l("inverse",e.placeholder[0])+l("dim",e.placeholder.slice(1)):l(["inverse","hidden"],"_"),n=this.userInput?this.userInputWithCursor:i,c=this.value??"";switch(this.state){case"validating":{const a=t?`${l("cyan",v)} `:"",o=t?l("cyan",V):"",s=l("dim",n),u=l("dim","Validating...");return`${r}${a}${s}
38
+ ${o} ${u}
39
+ `}case"error":{const a=this.error?` ${l("yellow",this.error)}`:"",o=t?`${l("yellow",v)} `:"",s=t?l("yellow",V):"";return`${r.trim()}
38
40
  ${o}${n}
39
41
  ${s}${a}
40
- `}case"submit":{const a=c?` ${u("dim",c)}`:"",o=t?u("gray",v):"";return`${r}${o}${a}`}case"cancel":{const a=c?` ${u(["strikethrough","dim"],c)}`:"",o=t?u("gray",v):"";return`${r}${o}${a}${c.trim()?`
41
- ${o}`:""}`}default:{const a=t?`${u("cyan",v)} `:"",o=t?u("cyan",U):"";return`${r}${a}${n}
42
+ `}case"submit":{const a=c?`${t?" ":""}${l("dim",c)}`:"",o=t?l("gray",v):"";return`${r}${o}${a}`}case"cancel":{const a=c?` ${l(["strikethrough","dim"],c)}`:"",o=t?l("gray",v):"";return`${r}${o}${a}${c.trim()?`
43
+ ${o}`:""}`}default:{const a=t?`${l("cyan",v)} `:"",o=t?l("cyan",V):"";return`${r}${a}${n}
42
44
  ${o}
43
- `}}}}).prompt();function oe(e){return e==null?[]:Array.isArray(e)?e:[e]}function Et(e,t,r,i){var n,c=e[t],a=~i.string.indexOf(t)?r==null||r===!0?"":String(r):typeof r=="boolean"?r:~i.boolean.indexOf(t)?r==="false"?!1:r==="true"||(e._.push((n=+r,n*0===0?n:r)),!!r):(n=+r,n*0===0?n:r);e[t]=c==null?a:Array.isArray(c)?c.concat(a):[c,a]}function _t(e,t){e=e||[],t=t||{};var r,i,n,c,a,o={_:[]},s=0,l=0,d=0,h=e.length;const m=t.alias!==void 0,p=t.unknown!==void 0,f=t.default!==void 0;if(t.alias=t.alias||{},t.string=oe(t.string),t.boolean=oe(t.boolean),m)for(r in t.alias)for(i=t.alias[r]=oe(t.alias[r]),s=0;s<i.length;s++)(t.alias[i[s]]=i.concat(r)).splice(s,1);for(s=t.boolean.length;s-- >0;)for(i=t.alias[t.boolean[s]]||[],l=i.length;l-- >0;)t.boolean.push(i[l]);for(s=t.string.length;s-- >0;)for(i=t.alias[t.string[s]]||[],l=i.length;l-- >0;)t.string.push(i[l]);if(f){for(r in t.default)if(c=typeof t.default[r],i=t.alias[r]=t.alias[r]||[],t[c]!==void 0)for(t[c].push(r),s=0;s<i.length;s++)t[c].push(i[s])}const b=p?Object.keys(t.alias):[];for(s=0;s<h;s++){if(n=e[s],n==="--"){o._=o._.concat(e.slice(++s));break}for(l=0;l<n.length&&n.charCodeAt(l)===45;l++);if(l===0)o._.push(n);else if(n.substring(l,l+3)==="no-"){if(c=n.substring(l+3),p&&!~b.indexOf(c))return t.unknown(n);o[c]=!1}else{for(d=l+1;d<n.length&&n.charCodeAt(d)!==61;d++);for(c=n.substring(l,d),a=n.substring(++d)||s+1===h||(""+e[s+1]).charCodeAt(0)===45||e[++s],i=l===2?[c]:c,d=0;d<i.length;d++){if(c=i[d],p&&!~b.indexOf(c))return t.unknown("-".repeat(l)+c);Et(o,c,d+1<i.length||a,t)}}}if(f)for(r in t.default)o[r]===void 0&&(o[r]=t.default[r]);if(m)for(r in o)for(i=t.alias[r]||[];i.length>0;)o[i.shift()]=o[r];return o}var Z={exports:{}},ke;function It(){if(ke)return Z.exports;ke=1;let e=process||{},t=e.argv||[],r=e.env||{},i=!(r.NO_COLOR||t.includes("--no-color"))&&(!!r.FORCE_COLOR||t.includes("--color")||e.platform==="win32"||(e.stdout||{}).isTTY&&r.TERM!=="dumb"||!!r.CI),n=(o,s,l=o)=>d=>{let h=""+d,m=h.indexOf(s,o.length);return~m?o+c(h,s,l,m)+s:o+h+s},c=(o,s,l,d)=>{let h="",m=0;do h+=o.substring(m,d)+l,m=d+s.length,d=o.indexOf(s,m);while(~d);return h+o.substring(m)},a=(o=i)=>{let s=o?n:()=>String;return{isColorSupported:o,reset:s("\x1B[0m","\x1B[0m"),bold:s("\x1B[1m","\x1B[22m","\x1B[22m\x1B[1m"),dim:s("\x1B[2m","\x1B[22m","\x1B[22m\x1B[2m"),italic:s("\x1B[3m","\x1B[23m"),underline:s("\x1B[4m","\x1B[24m"),inverse:s("\x1B[7m","\x1B[27m"),hidden:s("\x1B[8m","\x1B[28m"),strikethrough:s("\x1B[9m","\x1B[29m"),black:s("\x1B[30m","\x1B[39m"),red:s("\x1B[31m","\x1B[39m"),green:s("\x1B[32m","\x1B[39m"),yellow:s("\x1B[33m","\x1B[39m"),blue:s("\x1B[34m","\x1B[39m"),magenta:s("\x1B[35m","\x1B[39m"),cyan:s("\x1B[36m","\x1B[39m"),white:s("\x1B[37m","\x1B[39m"),gray:s("\x1B[90m","\x1B[39m"),bgBlack:s("\x1B[40m","\x1B[49m"),bgRed:s("\x1B[41m","\x1B[49m"),bgGreen:s("\x1B[42m","\x1B[49m"),bgYellow:s("\x1B[43m","\x1B[49m"),bgBlue:s("\x1B[44m","\x1B[49m"),bgMagenta:s("\x1B[45m","\x1B[49m"),bgCyan:s("\x1B[46m","\x1B[49m"),bgWhite:s("\x1B[47m","\x1B[49m"),blackBright:s("\x1B[90m","\x1B[39m"),redBright:s("\x1B[91m","\x1B[39m"),greenBright:s("\x1B[92m","\x1B[39m"),yellowBright:s("\x1B[93m","\x1B[39m"),blueBright:s("\x1B[94m","\x1B[39m"),magentaBright:s("\x1B[95m","\x1B[39m"),cyanBright:s("\x1B[96m","\x1B[39m"),whiteBright:s("\x1B[97m","\x1B[39m"),bgBlackBright:s("\x1B[100m","\x1B[49m"),bgRedBright:s("\x1B[101m","\x1B[49m"),bgGreenBright:s("\x1B[102m","\x1B[49m"),bgYellowBright:s("\x1B[103m","\x1B[49m"),bgBlueBright:s("\x1B[104m","\x1B[49m"),bgMagentaBright:s("\x1B[105m","\x1B[49m"),bgCyanBright:s("\x1B[106m","\x1B[49m"),bgWhiteBright:s("\x1B[107m","\x1B[49m")}};return Z.exports=a(),Z.exports.createColors=a,Z.exports}var Ft=It();const Tt=tt(Ft),{cyan:Ct,magenta:Rt,yellow:At}=Tt,H=[{name:"engine",display:"Engine",description:"with the PlayCanvas Engine API directly",color:At},{name:"react",display:"React",description:"with @playcanvas/react",color:Ct},{name:"web-components",display:"Web Components",description:"with @playcanvas/web-components",color:Rt}],je={physics:{engine:["assets"],react:["react"],"web-components":["assets"]}},V=[{name:"spinning-cube",display:"Spinning Cube",category:"Basics",description:"a rotating cube and a light"},{name:"interactive-sphere",display:"Interactive Sphere",category:"Basics",description:"orbit controls, a grid and pointer picking"},{name:"model-viewer",display:"Model Viewer",category:"Viewers",description:"a glTF model, orbit controls and a procedural sky"},{name:"splat-viewer",display:"Splat Viewer",category:"Viewers",description:"a Gaussian splat and orbit controls"},{name:"product-configurator",display:"Product Configurator",category:"Viewers",description:"product variants, materials and orbit controls"},{name:"physics-playground",display:"Physics Playground",category:"Games",description:"rigid bodies, collisions, spawning and reset",features:["physics"]},{name:"first-person-controller",display:"First-Person Controller",category:"Games",description:"first-person movement, mouse look and jumping",features:["physics"]},{name:"third-person-controller",display:"Third-Person Controller",category:"Games",description:"an animated character, follow camera and locomotion",features:["physics"]},{name:"sprite-game",display:"Sprite Game",category:"Games",description:"an orthographic camera, sprite animation and input"},{name:"scene-editor",display:"Scene Editor",category:"Tools",description:"selection and translate, rotate and scale gizmos"},{name:"vr-starter",display:"VR Starter",category:"XR",description:"immersive VR, controllers and locomotion"},{name:"ar-placement",display:"AR Placement",category:"XR",description:"mobile AR, hit testing and object placement"}],kt=async({argFormat:e,yes:t,cancel:r})=>{if(e&&H.some(n=>n.name===e))return e;if(e&&(!process.stdout.isTTY||t))throw new Error(`Unknown format: ${e}. Choose from: ${H.map(n=>n.name).join(", ")}`);if(t)return H[0].name;const i=await ae({message:e?`"${e}" isn't a valid format. Please choose from below: `:"Select a format:",options:H.map(n=>({label:n.color(n.display),value:n.name,hint:n.description}))});return K(i)&&r(),i},jt=async({argStarter:e,yes:t,cancel:r})=>{if(e&&V.some(n=>n.name===e))return e;if(!process.stdout.isTTY||t){if(!e)return V[0].name;throw new Error(`Unknown starter: ${e}. Choose from: ${V.map(n=>n.name).join(", ")}`)}const i=await ae({message:e?`"${e}" isn't a valid starter. Please choose from below: `:"Select a starter:",initialValue:V[0].name,options:V.map(n=>({label:n.display,value:n.name,hint:`${n.category} \xB7 ${n.description}`}))});return K(i)&&r(),i},Oe=e=>/^(?:@[a-z\d\-*~][a-z\d\-*._~]*\/)?[a-z\d\-~][a-z\d\-._~]*$/.test(e),ce=e=>e.trim().toLowerCase().replace(/\s+/g,"-").replace(/^[._]/,"").replace(/[^a-z\d\-~]+/g,"-"),Ot=async({targetDir:e,yes:t,cancel:r})=>{let i=y.basename(y.resolve(e));if(!Oe(i)){if(t)return ce(i);const n=await Ae({message:"Package name:",defaultValue:ce(i),placeholder:ce(i),validate:c=>{if(!c||!Oe(c))return"Invalid package.json name"}});if(K(n))return r();i=n}return i},le=e=>e.trim().replace(/\/+$/g,""),Mt=e=>{const t=x.readdirSync(e);return t.length===0||t.length===1&&t[0]===".git"},Bt=e=>{if(x.existsSync(e))for(const t of x.readdirSync(e))t!==".git"&&x.rmSync(y.resolve(e,t),{recursive:!0,force:!0})},Pt=async({argTargetDir:e,defaultTargetDir:t,yes:r,cancel:i})=>{let n=e?le(String(e)):void 0;if(!n&&r)return t;if(!n){const c=await Ae({message:"Project name:",defaultValue:t,placeholder:t});K(c)&&i(),n=le(c)}return n},Lt=async({targetDir:e,argOverwrite:t,yes:r,cancel:i})=>{if(x.existsSync(e)&&!Mt(e)){if(r&&!t)throw new Error(`Target directory "${e}" is not empty. Pass --overwrite to remove its contents.`);const n=t?"yes":await ae({message:(e==="."?"Current directory":`Target directory "${e}"`)+" is not empty. Please choose how to proceed:",options:[{label:"Cancel operation",value:"no"},{label:"Remove existing files and continue",value:"yes"},{label:"Ignore files and continue",value:"ignore"}]});if(K(n))return i();switch(n){case"yes":Bt(e);break;case"no":i();return}}},Nt=["scripts","dependencies","devDependencies","peerDependencies","engines"],Me="## Prerequisites",Dt={engine:"src/main.ts",react:"src/App.tsx","web-components":"src/main.ts"},Wt=e=>{const{cwd:t,targetDir:r,format:i,starter:n,packageName:c,skills:a,renameFiles:o}=e,s=y.resolve(t,r);x.mkdirSync(s,{recursive:!0});const l=process.env.npm_config_user_agent?.split("/")[0]??"npm";D.step(`Creating project in ${s}...`);const d=Ne(import.meta.url),h=y.dirname(d),m=[y.resolve(h,"../templates"),y.resolve(h,"../../templates")].find(g=>x.existsSync(y.join(g,i)));if(!m)throw new Error(`Format directory not found for: ${i}`);const p=y.join(m,i);for(const g of H)for(const T of V){const A=y.join(m,g.name,T.name),G=Dt[g.name];if(!G||!x.existsSync(y.join(A,G)))throw new Error(`Incomplete starter matrix: ${g.name}/${T.name}`);for(const Q of T.features??[]){const ue=je[Q]?.[g.name];if(!ue?.length||ue.some(Be=>!x.existsSync(y.join(m,"_features",Q,Be))))throw new Error(`Incomplete feature matrix: ${Q}/${g.name}`)}}const f=V.find(g=>g.name===n);if(!f)throw new Error(`Starter not found: ${n}`);const b=y.join(m,"_shared",n),F=y.join(p,"base"),C=y.join(p,n),O=(f.features??[]).flatMap(g=>je[g][i].map(T=>y.join(m,"_features",g,T))),B=[b,F,...O,C],k=B.map(g=>{const T=y.join(g,"package.json");return x.existsSync(T)?JSON.parse(x.readFileSync(T,"utf-8")):{}}),w=[k[1],k[0],...k.slice(2)];if(!x.existsSync(C))throw new Error(`Starter directory not found: ${i}/${n}`);for(const g of B)x.existsSync(g)&&x.cpSync(g,s,{recursive:!0});const S=y.join(b,"README.md"),$=y.join(s,"README.md");x.existsSync(S)&&x.writeFileSync($,x.readFileSync($,"utf8").replace(Me,`${x.readFileSync(S,"utf8").trimEnd()}
45
+ `}}}}).prompt();function ce(e){return e==null?[]:Array.isArray(e)?e:[e]}function _t(e,t,r,i){var n,c=e[t],a=~i.string.indexOf(t)?r==null||r===!0?"":String(r):typeof r=="boolean"?r:~i.boolean.indexOf(t)?r==="false"?!1:r==="true"||(e._.push((n=+r,n*0===0?n:r)),!!r):(n=+r,n*0===0?n:r);e[t]=c==null?a:Array.isArray(c)?c.concat(a):[c,a]}function It(e,t){e=e||[],t=t||{};var r,i,n,c,a,o={_:[]},s=0,u=0,d=0,h=e.length;const m=t.alias!==void 0,p=t.unknown!==void 0,f=t.default!==void 0;if(t.alias=t.alias||{},t.string=ce(t.string),t.boolean=ce(t.boolean),m)for(r in t.alias)for(i=t.alias[r]=ce(t.alias[r]),s=0;s<i.length;s++)(t.alias[i[s]]=i.concat(r)).splice(s,1);for(s=t.boolean.length;s-- >0;)for(i=t.alias[t.boolean[s]]||[],u=i.length;u-- >0;)t.boolean.push(i[u]);for(s=t.string.length;s-- >0;)for(i=t.alias[t.string[s]]||[],u=i.length;u-- >0;)t.string.push(i[u]);if(f){for(r in t.default)if(c=typeof t.default[r],i=t.alias[r]=t.alias[r]||[],t[c]!==void 0)for(t[c].push(r),s=0;s<i.length;s++)t[c].push(i[s])}const b=p?Object.keys(t.alias):[];for(s=0;s<h;s++){if(n=e[s],n==="--"){o._=o._.concat(e.slice(++s));break}for(u=0;u<n.length&&n.charCodeAt(u)===45;u++);if(u===0)o._.push(n);else if(n.substring(u,u+3)==="no-"){if(c=n.substring(u+3),p&&!~b.indexOf(c))return t.unknown(n);o[c]=!1}else{for(d=u+1;d<n.length&&n.charCodeAt(d)!==61;d++);for(c=n.substring(u,d),a=n.substring(++d)||s+1===h||(""+e[s+1]).charCodeAt(0)===45||e[++s],i=u===2?[c]:c,d=0;d<i.length;d++){if(c=i[d],p&&!~b.indexOf(c))return t.unknown("-".repeat(u)+c);_t(o,c,d+1<i.length||a,t)}}}if(f)for(r in t.default)o[r]===void 0&&(o[r]=t.default[r]);if(m)for(r in o)for(i=t.alias[r]||[];i.length>0;)o[i.shift()]=o[r];return o}var Z={exports:{}},je;function Ft(){if(je)return Z.exports;je=1;let e=process||{},t=e.argv||[],r=e.env||{},i=!(r.NO_COLOR||t.includes("--no-color"))&&(!!r.FORCE_COLOR||t.includes("--color")||e.platform==="win32"||(e.stdout||{}).isTTY&&r.TERM!=="dumb"||!!r.CI),n=(o,s,u=o)=>d=>{let h=""+d,m=h.indexOf(s,o.length);return~m?o+c(h,s,u,m)+s:o+h+s},c=(o,s,u,d)=>{let h="",m=0;do h+=o.substring(m,d)+u,m=d+s.length,d=o.indexOf(s,m);while(~d);return h+o.substring(m)},a=(o=i)=>{let s=o?n:()=>String;return{isColorSupported:o,reset:s("\x1B[0m","\x1B[0m"),bold:s("\x1B[1m","\x1B[22m","\x1B[22m\x1B[1m"),dim:s("\x1B[2m","\x1B[22m","\x1B[22m\x1B[2m"),italic:s("\x1B[3m","\x1B[23m"),underline:s("\x1B[4m","\x1B[24m"),inverse:s("\x1B[7m","\x1B[27m"),hidden:s("\x1B[8m","\x1B[28m"),strikethrough:s("\x1B[9m","\x1B[29m"),black:s("\x1B[30m","\x1B[39m"),red:s("\x1B[31m","\x1B[39m"),green:s("\x1B[32m","\x1B[39m"),yellow:s("\x1B[33m","\x1B[39m"),blue:s("\x1B[34m","\x1B[39m"),magenta:s("\x1B[35m","\x1B[39m"),cyan:s("\x1B[36m","\x1B[39m"),white:s("\x1B[37m","\x1B[39m"),gray:s("\x1B[90m","\x1B[39m"),bgBlack:s("\x1B[40m","\x1B[49m"),bgRed:s("\x1B[41m","\x1B[49m"),bgGreen:s("\x1B[42m","\x1B[49m"),bgYellow:s("\x1B[43m","\x1B[49m"),bgBlue:s("\x1B[44m","\x1B[49m"),bgMagenta:s("\x1B[45m","\x1B[49m"),bgCyan:s("\x1B[46m","\x1B[49m"),bgWhite:s("\x1B[47m","\x1B[49m"),blackBright:s("\x1B[90m","\x1B[39m"),redBright:s("\x1B[91m","\x1B[39m"),greenBright:s("\x1B[92m","\x1B[39m"),yellowBright:s("\x1B[93m","\x1B[39m"),blueBright:s("\x1B[94m","\x1B[39m"),magentaBright:s("\x1B[95m","\x1B[39m"),cyanBright:s("\x1B[96m","\x1B[39m"),whiteBright:s("\x1B[97m","\x1B[39m"),bgBlackBright:s("\x1B[100m","\x1B[49m"),bgRedBright:s("\x1B[101m","\x1B[49m"),bgGreenBright:s("\x1B[102m","\x1B[49m"),bgYellowBright:s("\x1B[103m","\x1B[49m"),bgBlueBright:s("\x1B[104m","\x1B[49m"),bgMagentaBright:s("\x1B[105m","\x1B[49m"),bgCyanBright:s("\x1B[106m","\x1B[49m"),bgWhiteBright:s("\x1B[107m","\x1B[49m")}};return Z.exports=a(),Z.exports.createColors=a,Z.exports}var Ct=Ft();const Tt=rt(Ct),{cyan:Rt,magenta:At,yellow:kt}=Tt,H=[{name:"engine",display:"Engine",description:"with the PlayCanvas Engine API directly",color:kt},{name:"react",display:"React",description:"with @playcanvas/react",color:Rt},{name:"web-components",display:"Web Components",description:"with @playcanvas/web-components",color:At}],Oe={physics:{engine:["assets"],react:["react"],"web-components":["assets"]}},U=[{name:"spinning-cube",display:"Spinning Cube",category:"Basics",description:"a rotating cube and a light"},{name:"interactive-sphere",display:"Interactive Sphere",category:"Basics",description:"orbit controls, a grid and pointer picking"},{name:"model-viewer",display:"Model Viewer",category:"Viewers",description:"a glTF model, orbit controls and a procedural sky"},{name:"splat-viewer",display:"Splat Viewer",category:"Viewers",description:"a Gaussian splat and orbit controls"},{name:"product-configurator",display:"Product Configurator",category:"Viewers",description:"product variants, materials and orbit controls"},{name:"physics-playground",display:"Physics Playground",category:"Games",description:"rigid bodies, collisions, spawning and reset",features:["physics"]},{name:"first-person-controller",display:"First-Person Controller",category:"Games",description:"first-person movement, mouse look and jumping",features:["physics"]},{name:"third-person-controller",display:"Third-Person Controller",category:"Games",description:"an animated character, follow camera and locomotion",features:["physics"]},{name:"sprite-game",display:"Sprite Game",category:"Games",description:"an orthographic camera, sprite animation and input"},{name:"scene-editor",display:"Scene Editor",category:"Tools",description:"selection and translate, rotate and scale gizmos"},{name:"vr-starter",display:"VR Starter",category:"XR",description:"immersive VR, controllers and locomotion"},{name:"ar-placement",display:"AR Placement",category:"XR",description:"mobile AR, hit testing and object placement"}],jt=async({argFormat:e,yes:t,cancel:r})=>{if(e&&H.some(n=>n.name===e))return e;if(e&&(!process.stdout.isTTY||t))throw new Error(`Unknown format: ${e}. Choose from: ${H.map(n=>n.name).join(", ")}`);if(t)return H[0].name;const i=await oe({message:e?`"${e}" isn't a valid format. Please choose from below: `:"Select a format:",options:H.map(n=>({label:n.color(n.display),value:n.name,hint:n.description}))});return K(i)&&r(),i},Ot=async({argStarter:e,yes:t,cancel:r})=>{if(e&&U.some(n=>n.name===e))return e;if(!process.stdout.isTTY||t){if(!e)return U[0].name;throw new Error(`Unknown starter: ${e}. Choose from: ${U.map(n=>n.name).join(", ")}`)}const i=await oe({message:e?`"${e}" isn't a valid starter. Please choose from below: `:"Select a starter:",initialValue:U[0].name,options:U.map(n=>({label:n.display,value:n.name,hint:`${n.category} \xB7 ${n.description}`}))});return K(i)&&r(),i},Be=e=>/^(?:@[a-z\d\-*~][a-z\d\-*._~]*\/)?[a-z\d\-~][a-z\d\-._~]*$/.test(e),le=e=>e.trim().toLowerCase().replace(/\s+/g,"-").replace(/^[._]/,"").replace(/[^a-z\d\-~]+/g,"-"),Bt=async({targetDir:e,yes:t,cancel:r})=>{let i=y.basename(y.resolve(e));if(!Be(i)){if(t)return le(i);const n=await ke({message:"Package name:",defaultValue:le(i),placeholder:le(i),validate:c=>{if(!c||!Be(c))return"Invalid package.json name"}});if(K(n))return r();i=n}return i},Q=e=>e.trim().replace(/\/+$/g,""),Mt=e=>{const t=$.readdirSync(e);return t.length===0||t.length===1&&t[0]===".git"},Pt=e=>{if($.existsSync(e))for(const t of $.readdirSync(e))t!==".git"&&$.rmSync(y.resolve(e,t),{recursive:!0,force:!0})},Lt=async({argTargetDir:e,defaultTargetDir:t,yes:r,cancel:i})=>{let n=e?Q(String(e)):void 0;if(!n&&r)return t;if(!n){const c=await ke({message:"Project name:",defaultValue:t,placeholder:t,validate:a=>{if(a&&!Q(a))return"Invalid project name"}});K(c)&&i(),n=Q(c)}return n},Nt=async({targetDir:e,argOverwrite:t,yes:r,cancel:i})=>{if($.existsSync(e)&&!Mt(e)){if(r&&!t)throw new Error(`Target directory "${e}" is not empty. Pass --overwrite to remove its contents.`);const n=t?"yes":await oe({message:(e==="."?"Current directory":`Target directory "${e}"`)+" is not empty. Please choose how to proceed:",options:[{label:"Cancel operation",value:"no"},{label:"Remove existing files and continue",value:"yes"},{label:"Ignore files and continue",value:"ignore"}]});if(K(n))return i();switch(n){case"yes":Pt(e);break;case"no":i();return}}},Dt=["scripts","dependencies","devDependencies","peerDependencies","engines"],Me="## Prerequisites",Wt={engine:"src/main.ts",react:"src/App.tsx","web-components":"src/main.ts"},Vt=e=>{const{cwd:t,targetDir:r,format:i,starter:n,packageName:c,skills:a,renameFiles:o}=e,s=y.resolve(t,r);$.mkdirSync(s,{recursive:!0});const u=process.env.npm_config_user_agent?.split("/")[0]??"npm";D.step(`Creating project in ${s}...`);const d=De(import.meta.url),h=y.dirname(d),m=[y.resolve(h,"../templates"),y.resolve(h,"../../templates")].find(g=>$.existsSync(y.join(g,i)));if(!m)throw new Error(`Format directory not found for: ${i}`);const p=y.join(m,i);for(const g of H)for(const C of U){const A=y.join(m,g.name,C.name),G=Wt[g.name];if(!G||!$.existsSync(y.join(A,G)))throw new Error(`Incomplete starter matrix: ${g.name}/${C.name}`);for(const ee of C.features??[]){const ue=Oe[ee]?.[g.name];if(!ue?.length||ue.some(Pe=>!$.existsSync(y.join(m,"_features",ee,Pe))))throw new Error(`Incomplete feature matrix: ${ee}/${g.name}`)}}const f=U.find(g=>g.name===n);if(!f)throw new Error(`Starter not found: ${n}`);const b=y.join(m,"_shared",n),F=y.join(p,"base"),T=y.join(p,n),O=(f.features??[]).flatMap(g=>Oe[g][i].map(C=>y.join(m,"_features",g,C))),M=[b,F,...O,T],k=M.map(g=>{const C=y.join(g,"package.json");return $.existsSync(C)?JSON.parse($.readFileSync(C,"utf-8")):{}}),w=[k[1],k[0],...k.slice(2)];if(!$.existsSync(T))throw new Error(`Starter directory not found: ${i}/${n}`);for(const g of M)$.existsSync(g)&&$.cpSync(g,s,{recursive:!0});const S=y.join(b,"README.md"),x=y.join(s,"README.md");$.existsSync(S)&&$.writeFileSync(x,$.readFileSync(x,"utf8").replace(Me,`${$.readFileSync(S,"utf8").trimEnd()}
44
46
 
45
- ${Me}`));for(const[g,T]of Object.entries(o)){const A=y.join(s,g);x.existsSync(A)&&x.renameSync(A,y.join(s,T))}const E=y.join(s,"package.json"),_=Object.assign({},...w);for(const g of Nt){const T=Object.assign({},...w.map(A=>A?.[g]));Object.keys(T).length?_[g]=T:delete _[g]}if(_.name=c,x.writeFileSync(E,JSON.stringify(_,null,4)+`
46
- `),a){const g=[y.resolve(h,"../skills"),y.resolve(h,"../../skills")].find(A=>x.existsSync(A));if(!g)throw new Error("Skills directory not found");const T=x.readdirSync(g,{withFileTypes:!0}).filter(A=>A.isDirectory()).map(A=>A.name);for(const A of[".claude/skills",".agents/skills"])for(const G of T)x.cpSync(y.join(g,G),y.join(s,A,G),{recursive:!0});D.step("Added PlayCanvas agent skills (opt out with --no-skills)")}let R=`Project created. Now run:
47
+ ${Me}`));for(const[g,C]of Object.entries(o)){const A=y.join(s,g);$.existsSync(A)&&$.renameSync(A,y.join(s,C))}const E=y.join(s,"package.json"),_=Object.assign({},...w);for(const g of Dt){const C=Object.assign({},...w.map(A=>A?.[g]));Object.keys(C).length?_[g]=C:delete _[g]}if(_.name=c,$.writeFileSync(E,JSON.stringify(_,null,4)+`
48
+ `),a){const g=[y.resolve(h,"../skills"),y.resolve(h,"../../skills")].find(A=>$.existsSync(A));if(!g)throw new Error("Skills directory not found");const C=$.readdirSync(g,{withFileTypes:!0}).filter(A=>A.isDirectory()).map(A=>A.name);for(const A of[".claude/skills",".agents/skills"])for(const G of C)$.cpSync(y.join(g,G),y.join(s,A,G),{recursive:!0});D.step("Added PlayCanvas agent skills (opt out with --no-skills)")}let R=`Project created. Now run:
47
49
  `;const I=y.isAbsolute(r)?s:y.relative(t,s);switch(s!==t&&(R+=`
48
- cd ${I.includes(" ")?`"${I}"`:I}`),l){case"yarn":R+=`
50
+ cd ${I.includes(" ")?`"${I}"`:I}`),u){case"yarn":R+=`
49
51
  yarn`,R+=`
50
52
  yarn dev`;break;default:R+=`
51
- ${l} install`,R+=`
52
- ${l} run dev`;break}vt(R)},L=_t(process.argv.slice(2),{alias:{b:"boilerplate",f:"format",h:"help",s:"starter",t:"template",y:"yes"},boolean:["help","overwrite","yes"],string:["format","starter","template","boilerplate"]}),Vt=process.cwd(),Ut=`Usage: npm create playcanvas@latest [OPTION]...
53
+ ${u} install`,R+=`
54
+ ${u} run dev`;break}St(R)},L=It(process.argv.slice(2),{alias:{b:"boilerplate",f:"format",h:"help",s:"starter",t:"template",y:"yes"},boolean:["help","overwrite","yes"],string:["format","starter","template","boilerplate"]}),Ut=process.cwd(),Gt=`Usage: npm create playcanvas@latest [OPTION]...
53
55
 
54
56
  Create a new PlayCanvas project with TypeScript.
55
57
  With no arguments, start the CLI in interactive mode.
@@ -61,4 +63,4 @@ Options:
61
63
  --no-skills omit the PlayCanvas agent skills
62
64
  -y, --yes skip the prompts and take the defaults
63
65
  -h, --help show help
64
- `,Gt={_gitignore:".gitignore",_npmrc:".npmrc"},Kt="playcanvas-project",Ht=async()=>{const e=L._[0]?le(String(L._[0])):void 0,t=L.format??L.template,r=L.starter??L.boilerplate,i=L.overwrite,n=L.yes;if(L.help){console.log(Ut);return}const c=()=>{wt("Operation cancelled"),process.exit(0)},a=await Pt({argTargetDir:e,defaultTargetDir:Kt,yes:n,cancel:c});await Lt({targetDir:a,argOverwrite:i,yes:n,cancel:c});const o=await Ot({targetDir:a,yes:n,cancel:c}),s=await kt({argFormat:t,yes:n,cancel:c}),l=await jt({argStarter:r,yes:n,cancel:c}),d=L.skills??!0;Wt({cwd:Vt,targetDir:a,format:s,starter:l,packageName:o,skills:d,renameFiles:Gt})};Ht().catch(e=>{console.error(e instanceof Error?e.message:e),process.exitCode=1});
66
+ `,Kt={_gitignore:".gitignore",_npmrc:".npmrc"},Ht="playcanvas-project",zt=async()=>{const e=L._[0]?Q(String(L._[0])):void 0,t=L.format??L.template,r=L.starter??L.boilerplate,i=L.overwrite,n=L.yes;if(L.help){console.log(Gt);return}const c=()=>{vt("Operation cancelled"),process.exit(0)},a=await Lt({argTargetDir:e,defaultTargetDir:Ht,yes:n,cancel:c});await Nt({targetDir:a,argOverwrite:i,yes:n,cancel:c});const o=await Bt({targetDir:a,yes:n,cancel:c}),s=await jt({argFormat:t,yes:n,cancel:c}),u=await Ot({argStarter:r,yes:n,cancel:c}),d=L.skills??!0;Vt({cwd:Ut,targetDir:a,format:s,starter:u,packageName:o,skills:d,renameFiles:Kt})};zt().catch(e=>{console.error(e instanceof Error?e.message:e),process.exitCode=1});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-playcanvas",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "description": "Scaffold a PlayCanvas project",
5
5
  "keywords": [
6
6
  "playcanvas",
@@ -42,13 +42,13 @@
42
42
  "typecheck": "tsc --noEmit"
43
43
  },
44
44
  "devDependencies": {
45
- "@clack/prompts": "^1.7.0",
45
+ "@clack/prompts": "^1.8.1",
46
46
  "@playcanvas/eslint-config": "3.0.0-beta.8",
47
- "@types/node": "22.20.1",
47
+ "@types/node": "22.20.4",
48
48
  "eslint": "^9.39.5",
49
49
  "mri": "^1.2.0",
50
50
  "picocolors": "^1.1.1",
51
- "prettier": "^3.9.6",
51
+ "prettier": "^3.9.9",
52
52
  "unbuild": "^3.6.1"
53
53
  },
54
54
  "engines": {
@@ -5,8 +5,7 @@ description: Use when adding transient visual effects or trails to a PlayCanvas
5
5
 
6
6
  # Effects and game feel
7
7
 
8
- Add transient effects as short-lived, self-owning entities triggered by application events, not as
9
- permanent scene fixtures.
8
+ Trigger transient effects through application events and reuse their resources.
10
9
 
11
10
  Reuse before authoring. Adapt the closest official particle example with `find-examples`, prefer the
12
11
  Engine's built-in particle component over a hand-written system, and discover any shipped trail or
@@ -25,13 +24,23 @@ the actual scene lighting and surface.
25
24
 
26
25
  ## Place and layer
27
26
 
28
- - Spawn an effect at the emitter's world mount point, either parent it there or copy the world
29
- transform once, and let it destroy itself when finished. Never leak emitters.
27
+ - Place an effect at the emitter's world mount point; parent it there or copy the world transform once.
30
28
  - Keep additive or transparent effects above the surface they sit on and biased toward the camera so
31
29
  they do not z-fight. Inspect soft transparency at grazing angles for hard edges, black quads, or
32
30
  dropout.
33
31
  - Size and orient effects from the emitter's calibrated bounds, not a guessed constant.
34
32
 
33
+ ## Pool and prewarm
34
+
35
+ - Create emitters, ribbons, flashes, materials, and meshes at load. Events check out an effect, set
36
+ its transform, and call `particlesystem.reset()` then `play()`; after its particles finish, call
37
+ `stop()` and return it to the pool.
38
+ - First-use shader compilation can stall a frame. Before gameplay, render every material and particle
39
+ variant through the relevant passes, on an offscreen rig or behind the ready overlay.
40
+ - Pulse or tint uniforms with `meshInstance.setParameter` or `material.setParameter`.
41
+ `material.update()` marks the whole material dirty and clears variants when defines or chunks
42
+ change; reserve it for material changes that require it.
43
+
35
44
  ## Moving trails
36
45
 
37
46
  - Treat built-in render primitives as blocking geometry for soft effects. Follow the closest
@@ -74,8 +83,10 @@ the actual scene lighting and surface.
74
83
 
75
84
  Trigger each effect through real gameplay input and confirm with a screenshot captured at its peak,
76
85
  not a saved filepath. Confirm the effect is visible from the gameplay camera and from a grazing
77
- angle, that emitters are gone after their lifetime (query the entity count), and that nothing leaves
78
- a black quad or a hard edge over the surface.
86
+ angle, that repeated events leave entity counts stable, and that nothing leaves a black quad or a
87
+ hard edge over the surface. Compare `graphicsDevice.shaders.length` after prewarming and after the
88
+ first real event; growth indicates a missed variant. Use two fixed poses and return screenshots at
89
+ accept-or-reject points only, not after every edit.
79
90
 
80
91
  Choose the authoring surface with the `build-app` skill; create effects through its entity and
81
92
  component primitives and own their lifecycle in the surface that spawned them.
@@ -41,8 +41,8 @@ details. Apply these stable conventions throughout the project.
41
41
  with real input after a rendered frame. Judge the camera basis or a projected fixed landmark, not
42
42
  world-space camera coordinates or internal angle signs; both can approve inverted behavior.
43
43
  - Materials cull back faces by default; counter-clockwise winding defines the front face.
44
- - Clone a material before per-instance tinting or edits, and assign the clone; never mutate one
45
- shared by other instances, including default and asset-imported materials.
44
+ - Clone and reassign a shared material before editing it for only some meshes, including default
45
+ and asset-imported materials. Per-mesh uniforms use `meshInstance.setParameter()` without cloning.
46
46
  - `setParameter` uploads raw values; author colours in sRGB and convert with `Color#linear()`
47
47
  before upload — do not hand-roll gamma.
48
48
  - Entities do not have an `aabb`. Union descendant `meshInstance.aabb` values after transforms have
@@ -26,26 +26,32 @@ shadow-map rendering and per-pixel dynamic lighting for that light entirely.
26
26
 
27
27
  ## Verify UVs before trusting this on real assets
28
28
 
29
- Procedural primitives auto-generate a usable UV set; imported GLB models often do not ship a second,
30
- non-overlapping UV channel suitable for a lightmap atlas. Read the installed engine's source for
31
- what its `Lightmapper` actually requires — which UV set it samples, whether it unwraps one for you —
32
- before assuming a real model will bake correctly. Do not carry a primitive-only test result into
33
- production assets unchecked.
29
+ The installed `Lightmapper` bakes a node only when every mesh instance on it has a second UV set
30
+ (`mesh.vertexBuffer.format.hasUv1`). A node missing one is skipped silently — no warning, no
31
+ lightmap — and its mesh simply stays dynamically lit, which a glance at a screenshot will not catch.
32
+ Procedural primitives generate that set; imported GLB models often do not. Check `hasUv1` on every
33
+ mesh you expect to bake, and unwrap or re-export the ones without it before the bake. Do not carry a
34
+ primitive-only test result into production assets unchecked.
34
35
 
35
36
  ## Choose the rung by budget, not by default
36
37
 
37
- On-device bake cost scales with baked-node count and lightmap resolution; estimate it against the
38
- startup budget before choosing a rung. Stay on-device unless:
38
+ On-device bake cost scales with baked-node count and lightmap resolution; measure it once on the
39
+ target device (`performance.now()` around `bake()`) and compare it with the startup budget before
40
+ choosing a rung. Stay on-device unless:
39
41
 
40
- - the estimated bake time does not fit the startup or first-paint budget on the target device, or
42
+ - the measured bake time does not fit the startup or first-paint budget on the target device, or
41
43
  - the result needs stylized material detail beyond lighting that the runtime `Lightmapper` cannot
42
44
  produce.
43
45
 
44
- Only then move to an offline bake shipped as assets, following the same shape as any other
45
- generated-asset pipeline: registry-keyed per-asset configs (a new asset is a config change, not new
46
- pipeline code), a CLI with per-target fast paths, staged size logging, worker-thread parallelism for
47
- build time, and a coverage self-audit that fails the bake when an expected output is missing or
48
- empty.
46
+ Only then move to an offline bake shipped as assets, and size the pipeline to the scene count. For
47
+ one deterministic scene the whole pipeline is a build script that computes or renders the maps and
48
+ writes them next to the other static assets, plus a loader; that is sufficient. Registry-keyed
49
+ per-asset configs, worker-thread parallelism and a coverage audit that fails the bake on a missing
50
+ output belong to multi-asset pipelines — add them when the second scene arrives, not before.
51
+
52
+ Offline maps are payload. State the compressed size delta in the change description and compare it
53
+ with the download or package budget; a modest lightmap set can double the archive of a small web
54
+ app.
49
55
 
50
56
  ## Keep dynamic objects consistent
51
57
 
@@ -19,11 +19,13 @@ Preserve the surface that owns the current entry point and lifecycle.
19
19
 
20
20
  ## Render at an intentional density
21
21
 
22
- PlayCanvas defaults `graphicsDevice.maxPixelRatio` to 1 for predictable fill-rate cost. In a
23
- quality-oriented browser app, set it deliberately after the application exists and before its
24
- initial automatic resize; `Math.min(window.devicePixelRatio, 2)` is a practical high-DPI starting
25
- point. Keep 1 only as a measured performance decision, and resize the canvas again after changing
26
- the value.
22
+ PlayCanvas defaults `graphicsDevice.maxPixelRatio` to 1 for predictable fill-rate cost. Set it
23
+ deliberately after the application exists and before its initial automatic resize, and call
24
+ `app.resizeCanvas()` after changing it. Fragment work scales with the square of the ratio, so
25
+ choose the value from a measured frame time on the target display, not from the display alone:
26
+ `Math.min(window.devicePixelRatio, 2)` suits a quality-first viewer on hardware that holds its
27
+ frame budget there, and 1 is the right value wherever it does not. Never cap above
28
+ `window.devicePixelRatio`; a larger value renders identically.
27
29
 
28
30
  Verify the real backbuffer in a browser: for automatic resolution, `canvas.width / canvas.clientWidth`
29
31
  and the height ratio should match the chosen pixel ratio. CSS dimensions and a screenshot filepath
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: light-scene
3
- description: Use when a PlayCanvas scene must read as a deliberate cinematic image rather than a flat asset preview, to set key and ambient lighting, sky and image-based lighting, shadows, tone mapping and exposure, and a restrained post-process grade of bloom, fog, and colour.
3
+ description: Use when a PlayCanvas scene must read as a deliberate image rather than a flat asset preview — photoreal and cinematic, or stylized, low-poly, and palette-driven — to set key and ambient lighting, sky and image-based lighting, shadows, tone mapping and exposure, water, and a restrained post-process grade of bloom, fog, and colour.
4
4
  ---
5
5
 
6
6
  # Cinematic rendering
@@ -9,6 +9,26 @@ A polished frame is lit and graded, not just populated. Treat lighting, sky, sha
9
9
  and post as one pass over the assembled scene, and tune every value against a real screenshot rather
10
10
  than from memory.
11
11
 
12
+ ## Confirm art direction before implementation
13
+
14
+ Follow the user's specified or approved direction. When it is unresolved, present concrete options
15
+ using references, mockups, or inexpensive rendered variants and get approval before substantial
16
+ implementation. Prefer a side-by-side variant sheet when cheap to produce. Work within the approved
17
+ direction and confirm significant departures before implementing them.
18
+
19
+ ## Choose the path from the brief
20
+
21
+ Read the reference before wiring any pipeline. A photoreal or cinematic brief takes the runtime HDR
22
+ path below. A brief whose reference is low-poly, flat-shaded, toon, or palette-driven — or whose
23
+ startup, frame, or payload budget rules out the runtime pipeline — takes the stylized path: banded
24
+ or flat shading through `override-shader-chunks`, a solid or gradient sky, static lighting through
25
+ `bake-lighting`, and no `Water` or `CameraFrame`. A remark such as "look at the water script" is a
26
+ pointer to inspect it; confirm any resulting change of art direction before integration. On either
27
+ path, dynamic objects and their attachments must sample or approximate the baked or ambient occlusion
28
+ so they dim to the same levels as the surfaces around them, grounded with contact shadows.
29
+
30
+ ## Runtime HDR path
31
+
12
32
  Prefer shipped building blocks over hand-written shaders and passes. Discover them with the
13
33
  `reuse-scripts` skill and adapt the closest official example with `find-examples`:
14
34
 
@@ -16,32 +36,22 @@ Prefer shipped building blocks over hand-written shaders and passes. Discover th
16
36
  - a procedural sky and image-based light for ambient colour and reflections;
17
37
  - a shadow catcher for grounding subjects that have no lit floor.
18
38
 
19
- For an outdoor scene with water, stop before authoring the scene and read both `reuse-scripts` and
20
- `find-examples`. Use `graphics/water.example.mjs` as the initial composition recipe: it already
21
- joins the production `Water` script, sky, sun, `CameraFrame`, water layer, scene depth, and
22
- reflection pipeline. Get that complete frame rendering before replacing its assets or tuning its
23
- look. A custom normal-mapped material, tessellated plane, or reflection-coloured mesh is not the
24
- baseline and does not substitute for `Water` when the brief asks for reflective water.
39
+ For an outdoor scene whose brief asks for reflective, photoreal water, stop before authoring the
40
+ scene and read both `reuse-scripts` and `find-examples`. Use `graphics/water.example.mjs` as the
41
+ initial composition recipe: it already joins the production `Water` script, sky, sun, `CameraFrame`,
42
+ water layer, scene depth, and reflection pipeline. Get that complete frame rendering before
43
+ replacing its assets or tuning its look; a custom normal-mapped material or tessellated plane is not
44
+ that baseline.
25
45
 
26
- Keep the two named production integrations visible in the implementation:
46
+ For that water scene, keep both production integrations visible in the implementation:
27
47
 
28
48
  - import and create `Water` from `playcanvas/scripts/esm/water.mjs` on the water entity, preserving
29
49
  the example's required render component, camera reference, water layer, depth map, and textures;
30
50
  - construct `CameraFrame` from `playcanvas` for the gameplay camera, set a deliberate tone map and
31
51
  restrained grade, call `update()`, and destroy it with the application.
32
52
 
33
- Do not continue to cosmetic tuning while either integration is missing or diagnostics report a
34
- shader, texture, or framebuffer error.
35
-
36
- ## Stylized and prebaked scenes
37
-
38
- When the art direction is deliberately flat or stylized rather than photoreal, or the platform's
39
- startup, frame, or payload budget rules out the runtime pipeline above, do not treat `Water`,
40
- `CameraFrame`, and the sky/IBL bake as mandatory. The sanctioned alternative is baked static
41
- lighting via the `bake-lighting` skill plus custom material shading via the `override-shader-chunks`
42
- skill. Either way, dynamic objects and their attachments must sample or approximate the baked
43
- occlusion so they dim to the same levels as the baked surfaces, grounded with contact shadows. Fall
44
- back to the runtime pipeline above as the default path when neither trigger holds.
53
+ Do not continue to cosmetic tuning while a required integration is missing or diagnostics report
54
+ a shader, texture, or framebuffer error.
45
55
 
46
56
  ## Light and expose
47
57
 
@@ -84,11 +94,16 @@ back to the runtime pipeline above as the default path when neither trigger hold
84
94
 
85
95
  ## Prove the look
86
96
 
97
+ For changes to an existing look, show old and new at the same pose in one image. If the difference
98
+ is not visible, report that before spending more time tuning.
99
+
87
100
  Tune against pixels, not numbers. Capture the intended framing with an image-returning browser
88
101
  screenshot tool and inspect the returned image, as `apply-conventions` and the local agent guide
89
- require; a saved filepath is not evidence. Confirm the subject is exposed and readable, shadows are
90
- present but not crushed, and the grade is restrained, from the real gameplay camera at the target
91
- resolution. Confirm the post-process camera keeps rendering as it orbits and pitches.
102
+ require; a saved filepath is not evidence. Use two fixed poses, capture with a headless browser, and
103
+ return images at accept-or-reject points only, not after every edit. Confirm the subject is exposed
104
+ and readable, shadows are present but not crushed, and the grade is restrained, from the real
105
+ gameplay camera at the target resolution. Confirm the post-process camera keeps rendering as it
106
+ orbits and pitches.
92
107
 
93
108
  Choose the authoring surface with the `build-app` skill and set lights, camera, and scene settings
94
109
  through its own primitives before reaching into the Engine.
@@ -13,12 +13,18 @@ override is the default for everything else, since it is cheaper and stays integ
13
13
 
14
14
  ## Pin the version, read the source
15
15
 
16
- Set `material.shaderChunksVersion` to the installed engine's major.minor (for example `'2.21'`)
16
+ Set `material.shaderChunksVersion` to the installed engine's major.minor (for example `'2.22'`)
17
17
  before shipping any override. Chunk names and their contracts — what a chunk defines, what globals
18
18
  it reads or writes — are version-keyed and change between releases. Read the installed `playcanvas`
19
19
  source for the exact chunk being touched; do not recall its contract from memory or an older
20
20
  version's documentation.
21
21
 
22
+ ## Clone before overriding a shared material
23
+
24
+ Clone a material that other mesh instances share — including default and asset-imported materials —
25
+ before changing its chunks, properties, or material-level parameters for only some meshes, and assign
26
+ the clone. For per-mesh uniforms, use `meshInstance.setParameter()` without cloning the material.
27
+
22
28
  ## Set chunks through the documented accessor
23
29
 
24
30
  Use `material.getShaderChunks(SHADERLANGUAGE_GLSL).set(name, source)`, and the WGSL equivalent —
@@ -15,6 +15,12 @@ Read `app.stats.drawCalls.total` (or the `forward`/`depth`/`shadow` breakdown) b
15
15
  anything, or drop in `MiniStats` for a live overlay. Every rung below is proved against this number,
16
16
  not against intuition about what "looks expensive."
17
17
 
18
+ Draw count alone does not identify the bottleneck. Test sensitivity to pixel cost by halving the
19
+ effective pixel ratio (`Math.min(graphicsDevice.maxPixelRatio, window.devicePixelRatio)`) and calling
20
+ `app.resizeCanvas()`. Verify the backbuffer shrank and compare frame times at the same scene state.
21
+ If frame time improves materially, prioritize resolution, multisampling and per-pixel shader cost.
22
+ Otherwise profile CPU and GPU work before choosing a rung; restore the original ratio after the test.
23
+
18
24
  ## Rung 1: stop drawing invisible things
19
25
 
20
26
  An element at opacity 0 still submits a draw call — its mesh instance exists and is still in a
@@ -1,12 +1,12 @@
1
1
  ---
2
2
  name: reuse-scripts
3
- description: Use before implementing non-core PlayCanvas behavior to discover and reuse production scripts shipped with the installed Engine, reproduce their official example integration, and verify the result against the installed version.
3
+ description: Use before implementing non-core PlayCanvas behavior to discover the installed Engine's curated scripts and reuse or adapt those that fit the required behavior and approved art direction.
4
4
  ---
5
5
 
6
6
  # Engine scripts
7
7
 
8
- The installed `playcanvas` package ships production scripts under `scripts/esm/**`. Discover the
9
- current set rather than maintaining a stale list:
8
+ Before writing behavior from scratch, inspect the curated scripts shipped with the installed
9
+ `playcanvas` package under `scripts/esm/**`. Discover the current set:
10
10
 
11
11
  ```sh
12
12
  rg 'static scriptName =' node_modules/playcanvas/scripts/esm \
@@ -19,35 +19,39 @@ is a `Script`; the parsers below `scripts/esm/parsers` are plain classes registe
19
19
  handler instead.
20
20
 
21
21
  Only import from `scripts/esm/**`. Legacy sibling directories depend on the global Engine namespace.
22
- After selecting a script, use `find-examples` to locate its matching versioned Engine example.
22
+ After selecting a script, use `find-examples` to locate its matching versioned Engine example. When
23
+ `node_modules/playcanvas` is a linked or source checkout, its `examples/src/examples/**` are already
24
+ on disk; read them there and skip the fetch.
23
25
 
24
- Use a relevant shipped script as the default implementation. If it cannot own the whole feature,
25
- preserve its input signs, angular damping, bounds, and lifecycle invariants in the custom portion
26
- instead of replacing them from memory.
26
+ ## Check fit before integration
27
27
 
28
- ## Reproduce the reference integration
28
+ Compare candidates with the required behavior, approved art direction, and runtime constraints.
29
+ Reuse a suitable script directly or adapt its configuration and extension points. If none fits,
30
+ state the limitation and implement only the missing behavior, preserving reusable parts and their
31
+ lifecycle, bounds, and input invariants.
32
+
33
+ For visual behavior, resolve an unclear art direction with the user before substantial implementation.
34
+ Present references, mockups, or inexpensive variants; follow existing approval and confirm significant
35
+ departures. A script's default appearance is only a starting point for assessing its fit.
36
+
37
+ ## Adapt the reference integration
29
38
 
30
39
  Treat the selected script source and its closest official example as complementary references:
31
40
 
32
41
  1. Read the source for exports, properties, defaults, fallbacks, required components, and lifecycle.
33
42
  2. Read the example for assets, entity references, mesh requirements, layer ordering, scene settings,
34
43
  and render-pipeline setup.
35
- 3. Reproduce the smallest working baseline before customizing it. Preserve defaults that are not
36
- deliberately changed, and tune one property group at a time.
44
+ 3. Integrate the selected behavior with its required components, assets, and passes. Adapt the look
45
+ to the approved direction and preserve defaults and dependencies that still apply.
37
46
  4. After a rendered frame, fail on console, shader, or missing-asset diagnostics. Exercise the
38
47
  behavior with real input where applicable and inspect returned screenshots from representative
39
48
  views at the final backbuffer density.
40
- 5. Compare the result with the official example when visual quality matters. Keep iterating or
41
- report the gap; a running fallback is not proof of correct integration.
49
+ 5. Use the example to check integration correctness and the user's approved direction to judge the
50
+ final look. Report any remaining gap.
42
51
 
43
52
  If no matching example exists, state that and derive the integration from installed source instead
44
53
  of inventing it from memory.
45
54
 
46
- For an outdoor water scene, start from `graphics/water.example.mjs` as one integration recipe. It
47
- combines `Water`, `ProceduralSky`, `CameraControls`, `CameraFrame`, a dedicated water layer, scene
48
- depth, normal and caustics textures, and conservative rendering defaults. Establish that complete
49
- baseline before adapting its camera, assets, time of day, or water tuning.
50
-
51
55
  ## Preserve grouped defaults
52
56
 
53
57
  Grouped property updates differ by authoring surface. Read the selected reference and preserve
@@ -1,37 +1,17 @@
1
1
  # Direct Engine projects
2
2
 
3
- Import the named script class and attach it to the owning entity:
4
-
5
- ```ts
6
- import { CameraControls } from 'playcanvas/scripts/esm/camera-controls.mjs';
7
-
8
- camera.addComponent('script');
9
- const controls = camera.script?.create(CameraControls);
10
- ```
3
+ Import the selected class from its installed module, add a script component to the owning entity,
4
+ and attach it with `entity.script.create()`. Preserve an existing script component.
11
5
 
12
6
  Some production helpers are core Engine exports rather than scripts. Follow the matching example's
13
- construction style instead of forcing them through `script.create`:
14
-
15
- ```ts
16
- import { CameraFrame } from 'playcanvas';
17
-
18
- const frame = new CameraFrame(app, camera.camera!);
19
- frame.rendering.sceneDepthMap = true;
20
- frame.update();
21
- ```
7
+ construction and teardown contract instead of forcing them through `script.create`.
22
8
 
23
9
  Keep script constructors at module scope. `create` takes two distinct option keys that are not
24
10
  interchangeable: `properties` is assigned straight onto the instance, while `attributes` supplies
25
11
  declared `@attribute` fields. Passing the wrong one silently does nothing.
26
12
 
27
- Prefer mutating an existing grouped property when only a few fields change:
28
-
29
- ```ts
30
- import { ProceduralSky } from 'playcanvas/scripts/esm/sky/procedural-sky.mjs';
31
-
32
- const sky = skyEntity.script?.create(ProceduralSky);
33
- if (sky) sky.sunLight = sun;
34
- ```
13
+ Prefer mutating an existing grouped property when only a few fields change; preserve other defaults
14
+ and follow any update or rebuild contract in the selected source.
35
15
 
36
16
  If the installed script subpaths have no declarations, declare only the patterns the project uses:
37
17
 
@@ -1,17 +1,7 @@
1
1
  # React projects
2
2
 
3
- Import the named script class and attach it through React:
4
-
5
- ```tsx
6
- import { Entity } from '@playcanvas/react';
7
- import { Camera, Script } from '@playcanvas/react/components';
8
- import { CameraControls } from 'playcanvas/scripts/esm/camera-controls.mjs';
9
-
10
- <Entity name="Camera" position={[4, 1, 4]}>
11
- <Camera />
12
- <Script script={CameraControls} />
13
- </Entity>;
14
- ```
3
+ Import the selected class from its installed module. Render `Script` from
4
+ `@playcanvas/react/components` inside the owning `Entity`, passing the class through its `script` prop.
15
5
 
16
6
  Keep imported or custom script constructors at module scope. Declaring a class inside a component
17
7
  creates a new identity on render and can recreate the script.
@@ -1,20 +1,7 @@
1
1
  # Web Components projects
2
2
 
3
- Expose an Engine script as an ES module and register it as an asset:
4
-
5
- ```html
6
- <pc-app>
7
- <pc-asset src="/scripts/camera-controls.mjs"></pc-asset>
8
- <pc-scene>
9
- <pc-entity name="Camera">
10
- <pc-camera></pc-camera>
11
- <pc-script>
12
- <pc-script-instance name="cameraControls" move-speed="4"></pc-script-instance>
13
- </pc-script>
14
- </pc-entity>
15
- </pc-scene>
16
- </pc-app>
17
- ```
3
+ Expose the selected script as an ES module and register its URL with a `pc-asset` directly under
4
+ `pc-app`. Attach a `pc-script` to the owning `pc-entity`, with a `pc-script-instance` for the class.
18
5
 
19
6
  The `name` must equal the class's static `scriptName`. Extra kebab-case attributes map to camelCase
20
7
  script properties. Use the `attributes` JSON attribute for nested or reserved properties; it
@@ -1,16 +1,35 @@
1
1
  ---
2
2
  name: verify-pixels
3
- description: Use when changing PlayCanvas rendering code that must not change the rendered image — draw-call optimization, hardware instancing, shader or material refactors, or asset pipeline swaps — to prove the output unchanged before shipping.
3
+ description: Use when changing PlayCanvas rendering code that must not visibly change the rendered image — draw-call optimization, hardware instancing, shader or material refactors, noise or lightmap bakes, or asset pipeline swaps — before shipping.
4
4
  ---
5
5
 
6
6
  # Prove pixels unchanged
7
7
 
8
- A rendering change that must not alter the image is not proven by eyeballing a screenshot at one
9
- pose. Build a deterministic capture, compare byte-exact counts across a pose × phase matrix, and
10
- prove the capture harness itself is trustworthy before trusting what it reports about the change.
8
+ Choose the gate before building the capture harness. Both classes require controlled captures
9
+ that cover the affected surfaces.
10
+
11
+ ## Classify the change
12
+
13
+ | Change | Gate |
14
+ | --- | --- |
15
+ | Batching, instancing, mesh indexing, chunking, pooling that preserves RNG order, an asset format swap with identical decoded data | Byte-exact matrix |
16
+ | Re-rolled or re-sampled procedural noise, a changed RNG consumption order, math moved between CPU and GPU, changed precision, fewer octaves or samples | Not byte-gateable: side-by-side review |
17
+
18
+ For the second class, sampled values or precision can change. Report it as "not byte-gateable" and
19
+ use the side-by-side gate after the shared capture setup below; do not invent uncalibrated perceptual
20
+ thresholds to approve it.
21
+
22
+ ## Cover every touched surface
23
+
24
+ List every touched material or surface and a capture pose it dominates. Use at least two
25
+ representative poses, adding as many as coverage requires; a horizon strip does not cover a water
26
+ shader change. For animated surfaces, include a second phase mid-animation.
11
27
 
12
28
  ## Make the frame deterministic
13
29
 
30
+ For both gates, load assets fully and match the scene state, camera, canvas size, lighting, and
31
+ animation time between builds. Seed incidental randomness so only the intended change varies.
32
+
14
33
  - Drive every animated shader or vertex effect from one app-owned time value, never `Date.now()` or
15
34
  `performance.now()` read inside the render path, so a captured phase is exactly reproducible.
16
35
  - Freeze the clock with `app.timeScale = 0` before capturing; nothing should advance between frames
@@ -18,33 +37,34 @@ prove the capture harness itself is trustworthy before trusting what it reports
18
37
  - Step frames explicitly: set `app.autoRender = false` once, then set `app.renderNextFrame = true`
19
38
  before each frame you want rendered. The engine renders exactly that frame and clears the flag —
20
39
  do not rely on the free-running render loop plus a timed screenshot.
21
- - Read the rendered pixels with `await device.readPixelsAsync(x, y, w, h, pixels)` against the exact
22
- backbuffer, not a re-encoded screenshot (e.g. `canvas.toDataURL`) that can introduce compression or
23
- colour-management differences the eye won't catch. In the installed engine this method lives on
24
- `WebglGraphicsDevice`, not the base `GraphicsDevice` type, so narrow to it (or branch on
25
- `device.isWebGL2`) before calling; a WebGPU project needs its own equivalent readback. If the
26
- project already has a screenshot path, hold it to the same rule: fixed size, fixed pose, no lossy
27
- step before the byte comparison.
28
40
 
29
- ## Build a pose × phase matrix
41
+ ## Gate and report the byte-exact class
30
42
 
31
- Single-pose, single-frame proof cannot see everything a rendering refactor can break. Choose at
32
- least two representative camera poses — angles that would expose a dropped instance, a wrong batch
33
- bound, or a seam differently — and at least two animation phases, including one mid-animation, not
34
- only frame zero. Capture every pose × phase pair for the build before the change and the build after
35
- it, with the same canvas size, camera, and lighting each time.
36
-
37
- ## Run a same-build control first
43
+ Read the exact backbuffer with `await device.readPixelsAsync(x, y, w, h, pixels)`. In the installed
44
+ engine this method lives on `WebglGraphicsDevice`, so narrow to it; WebGPU needs an equivalent
45
+ readback. An existing capture path must preserve raw pixels without colour conversion or lossy
46
+ encoding before comparison.
38
47
 
39
48
  Before trusting any diff between the old and new build, capture the same pose × phase matrix twice
40
49
  from the *unmodified* build. Two captures of identical, frozen state must be bit-identical. If they
41
50
  are not, the capture path itself is the source of noise — an unseeded animation, an asset still
42
51
  loading, a GPU timing race — and must be fixed before it can say anything about the real change.
43
52
 
44
- ## Gate and report
45
-
46
53
  Byte-compare each pose × phase pair between the two builds; do not diff by looking. Zero differing
47
54
  pixels passes outright. Any nonzero diff must be reviewed on-screen, and its cause and extent stated
48
55
  in the change description — never merged silently. Report the actual count every time, for example
49
56
  "0 of 65536 pixels differ" or "312 of 65536 pixels differ, confined to the object's silhouette edge".
50
57
  "Looks the same" or "no visible difference" is not a result.
58
+
59
+ ## Review the other class side by side
60
+
61
+ For each planned pose and phase, show the controlled old and new captures together in one image
62
+ for the user's accept or reject. State what differs and why, for example "noise tile replaces
63
+ runtime fbm: pattern period changed, tint and amplitude match". If no difference is visible, report
64
+ that. Skip the cross-build byte comparison for this class.
65
+
66
+ ## Keep the harness small
67
+
68
+ Reuse the project's capture harness when available. Keep harness code out of the shipped bundle,
69
+ behind a dev-only import or in a tools directory, and list its files in the change description.
70
+ Capture with a headless browser and return images only at accept-or-reject points, not after every edit.
@@ -15,14 +15,14 @@
15
15
  "typecheck": "tsc --noEmit"
16
16
  },
17
17
  "dependencies": {
18
- "playcanvas": "^2.22.0"
18
+ "playcanvas": "^2.22.6"
19
19
  },
20
20
  "devDependencies": {
21
21
  "@playcanvas/eslint-config": "3.0.0-beta.8",
22
22
  "eslint": "^9.39.5",
23
- "prettier": "^3.9.6",
23
+ "prettier": "^3.9.9",
24
24
  "typescript": "~6.0.3",
25
- "vite": "^8.2.2"
25
+ "vite": "^8.3.1"
26
26
  },
27
27
  "engines": {
28
28
  "node": ">=22.23.2"
@@ -7,7 +7,7 @@ import { useState } from 'react';
7
7
  import { ArPlacement } from './ar-placement';
8
8
  import './starter.css';
9
9
 
10
- function App() {
10
+ function Scene() {
11
11
  const [camera, setCamera] = useState<PcEntity | null>(null);
12
12
  const [preview, setPreview] = useState<PcEntity | null>(null);
13
13
  const surface = useMaterial({ diffuse: '#474d57' });
@@ -16,29 +16,37 @@ function App() {
16
16
 
17
17
  return (
18
18
  <>
19
- <Application graphicsDeviceOptions={{ alpha: true, xrCompatible: true }}>
20
- <Entity name="camera" ref={setCamera} position={[3, 2.4, 4]} rotation={[-20, 37, 0]}>
21
- <Camera clearColor="#0d141f" />
19
+ <Entity name="camera" ref={setCamera} position={[3, 2.4, 4]} rotation={[-20, 37, 0]}>
20
+ <Camera clearColor="#0d141f" />
21
+ </Entity>
22
+ <Entity name="light" rotation={[45, 30, 0]}>
23
+ <Light type="directional" intensity={2} />
24
+ </Entity>
25
+ <Entity name="preview" ref={setPreview}>
26
+ <Entity name="surface" scale={[4, 0.15, 4]}>
27
+ <Render type="cylinder" material={surface} />
28
+ </Entity>
29
+ <Entity name="box" position={[0, 0.65, 0]}>
30
+ <Render type="box" material={cyan} />
22
31
  </Entity>
23
- <Entity name="light" rotation={[45, 30, 0]}>
24
- <Light type="directional" intensity={2} />
32
+ <Entity name="reticle" position={[0, 0.12, 0]} scale={[1.5, 0.03, 1.5]}>
33
+ <Render type="cylinder" material={coral} />
25
34
  </Entity>
26
- <Entity name="preview" ref={setPreview}>
27
- <Entity name="surface" scale={[4, 0.15, 4]}>
28
- <Render type="cylinder" material={surface} />
29
- </Entity>
30
- <Entity name="box" position={[0, 0.65, 0]}>
31
- <Render type="box" material={cyan} />
32
- </Entity>
33
- <Entity name="reticle" position={[0, 0.12, 0]} scale={[1.5, 0.03, 1.5]}>
34
- <Render type="cylinder" material={coral} />
35
- </Entity>
35
+ </Entity>
36
+ {camera && preview && (
37
+ <Entity name="placement">
38
+ <Script script={ArPlacement} camera={camera} preview={preview} />
36
39
  </Entity>
37
- {camera && preview && (
38
- <Entity name="placement">
39
- <Script script={ArPlacement} camera={camera} preview={preview} />
40
- </Entity>
41
- )}
40
+ )}
41
+ </>
42
+ );
43
+ }
44
+
45
+ function App() {
46
+ return (
47
+ <>
48
+ <Application graphicsDeviceOptions={{ alpha: true, xrCompatible: true }}>
49
+ <Scene />
42
50
  </Application>
43
51
  <div className="hud">
44
52
  <section className="panel">
@@ -15,20 +15,20 @@
15
15
  "typecheck": "tsc --noEmit"
16
16
  },
17
17
  "dependencies": {
18
- "@playcanvas/react": "^0.11.5",
19
- "playcanvas": "^2.22.0",
20
- "react": "^19.2.8",
21
- "react-dom": "^19.2.8"
18
+ "@playcanvas/react": "^0.11.7",
19
+ "playcanvas": "^2.22.6",
20
+ "react": "^19.3.0",
21
+ "react-dom": "^19.3.0"
22
22
  },
23
23
  "devDependencies": {
24
24
  "@playcanvas/eslint-config": "3.0.0-beta.8",
25
- "@types/react": "^19.2.18",
26
- "@types/react-dom": "^19.2.7",
25
+ "@types/react": "^19.3.0",
26
+ "@types/react-dom": "^19.3.0",
27
27
  "@vitejs/plugin-react": "^6.1.1",
28
28
  "eslint": "^9.39.5",
29
- "prettier": "^3.9.6",
29
+ "prettier": "^3.9.9",
30
30
  "typescript": "~6.0.3",
31
- "vite": "^8.2.2"
31
+ "vite": "^8.3.1"
32
32
  },
33
33
  "engines": {
34
34
  "node": ">=22.23.2"
@@ -5,25 +5,33 @@ import { useMaterial } from '@playcanvas/react/hooks';
5
5
  import { SpriteGame } from './sprite-game';
6
6
  import './starter.css';
7
7
 
8
- function App() {
8
+ function Scene() {
9
9
  const dirt = useMaterial({ emissive: '#734729' });
10
10
  const grass = useMaterial({ emissive: '#479e57' });
11
11
 
12
+ return (
13
+ <>
14
+ <Entity name="camera" position={[0, 0, 10]}>
15
+ <Camera projection={1} orthoHeight={4.4} clearColor="#5cb9e6" />
16
+ </Entity>
17
+ <Entity name="dirt" position={[0, -3.39, 0]} scale={[20, 2.6, 1]}>
18
+ <Render type="box" material={dirt} />
19
+ </Entity>
20
+ <Entity name="grass" position={[0, -2, 0]} scale={[20, 0.18, 1]}>
21
+ <Render type="box" material={grass} />
22
+ </Entity>
23
+ <Entity name="game" position={[0, -2.15, 0]}>
24
+ <Script script={SpriteGame} />
25
+ </Entity>
26
+ </>
27
+ );
28
+ }
29
+
30
+ function App() {
12
31
  return (
13
32
  <>
14
33
  <Application>
15
- <Entity name="camera" position={[0, 0, 10]}>
16
- <Camera projection={1} orthoHeight={4.4} clearColor="#5cb9e6" />
17
- </Entity>
18
- <Entity name="dirt" position={[0, -3.39, 0]} scale={[20, 2.6, 1]}>
19
- <Render type="box" material={dirt} />
20
- </Entity>
21
- <Entity name="grass" position={[0, -2, 0]} scale={[20, 0.18, 1]}>
22
- <Render type="box" material={grass} />
23
- </Entity>
24
- <Entity name="game" position={[0, -2.15, 0]}>
25
- <Script script={SpriteGame} />
26
- </Entity>
34
+ <Scene />
27
35
  </Application>
28
36
  <div className="hud">
29
37
  <section className="panel">
@@ -15,15 +15,15 @@
15
15
  "typecheck": "tsc --noEmit"
16
16
  },
17
17
  "dependencies": {
18
- "@playcanvas/web-components": "^0.21.0",
19
- "playcanvas": "^2.22.0"
18
+ "@playcanvas/web-components": "^0.26.0",
19
+ "playcanvas": "^2.22.6"
20
20
  },
21
21
  "devDependencies": {
22
22
  "@playcanvas/eslint-config": "3.0.0-beta.8",
23
23
  "eslint": "^9.39.5",
24
- "prettier": "^3.9.6",
24
+ "prettier": "^3.9.9",
25
25
  "typescript": "~6.0.3",
26
- "vite": "^8.2.2"
26
+ "vite": "^8.3.1"
27
27
  },
28
28
  "engines": {
29
29
  "node": ">=22.23.2"
@@ -9,7 +9,7 @@
9
9
  <body>
10
10
  <pc-app>
11
11
  <!-- Materials are declared once and referenced by id -->
12
- <pc-material id="sphere-material" diffuse="lightgrey"></pc-material>
12
+ <pc-material id="sphere-material" diffuse="#e6edf9"></pc-material>
13
13
 
14
14
  <pc-scene>
15
15
  <!-- Camera. Orbit controls are attached in src/main.ts -->
@@ -8,7 +8,7 @@
8
8
  </head>
9
9
  <body>
10
10
  <!-- Antialiasing is disabled - splat rendering is fragment bound, so MSAA costs a lot and adds little -->
11
- <pc-app antialias="false" high-resolution="false">
11
+ <pc-app antialias="false" max-pixel-ratio="1">
12
12
  <!-- Hosted so the template stays small. Capture or edit your own splat at
13
13
  https://superspl.at, then drop the .sog or .ply in public/ and point this at it instead -->
14
14
  <pc-asset id="splat" src="https://developer.playcanvas.com/assets/toy-cat.sog"></pc-asset>