create-playcanvas 0.11.0 → 0.11.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/README.md +15 -2
- package/dist/index.mjs +41 -38
- package/package.json +5 -2
- package/templates/_features/physics/assets/public/ammo/SOURCE.md +4 -1
- package/templates/{react/interactive-sphere/src/App.css → _shared/interactive-sphere/src/counter.css} +0 -6
- package/templates/engine/base/README.md +1 -1
- package/templates/engine/base/vite.config.ts +3 -0
- package/templates/engine/first-person-controller/src/main.ts +2 -3
- package/templates/engine/interactive-sphere/src/App.ts +1 -1
- package/templates/engine/interactive-sphere/src/main.ts +3 -5
- package/templates/engine/model-viewer/src/main.ts +3 -3
- package/templates/engine/physics-playground/src/main.ts +2 -3
- package/templates/engine/spinning-cube/src/main.ts +1 -1
- package/templates/engine/splat-viewer/src/main.ts +1 -1
- package/templates/engine/third-person-controller/src/main.ts +2 -3
- package/templates/react/base/README.md +1 -1
- package/templates/react/base/vite.config.ts +3 -0
- package/templates/react/interactive-sphere/src/App.tsx +5 -6
- package/templates/react/interactive-sphere/src/Scene.tsx +1 -1
- package/templates/react/model-viewer/src/App.tsx +4 -2
- package/templates/react/spinning-cube/src/App.tsx +2 -0
- package/templates/react/splat-viewer/src/App.tsx +2 -0
- package/templates/web-components/base/README.md +1 -1
- package/templates/web-components/base/vite.config.ts +3 -0
- package/templates/web-components/first-person-controller/index.html +1 -6
- package/templates/web-components/interactive-sphere/src/main.ts +2 -2
- package/templates/web-components/model-viewer/index.html +2 -2
- package/templates/web-components/model-viewer/src/main.ts +0 -1
- package/templates/web-components/physics-playground/index.html +1 -6
- package/templates/web-components/spinning-cube/src/main.ts +0 -1
- package/templates/web-components/splat-viewer/src/main.ts +0 -1
- package/templates/web-components/third-person-controller/index.html +1 -6
- package/templates/_features/physics/assets/public/ammo/ammo.js +0 -973
- package/templates/engine/interactive-sphere/index.html +0 -13
- package/templates/engine/interactive-sphere/src/style.css +0 -28
- package/templates/engine/model-viewer/index.html +0 -13
- package/templates/engine/model-viewer/src/style.css +0 -8
- package/templates/engine/spinning-cube/index.html +0 -13
- package/templates/engine/spinning-cube/src/style.css +0 -8
- package/templates/engine/splat-viewer/index.html +0 -13
- package/templates/engine/splat-viewer/src/style.css +0 -8
- package/templates/react/base/.prettierignore +0 -3
- package/templates/react/base/_gitignore +0 -24
- package/templates/react/base/_npmrc +0 -1
- package/templates/react/base/prettier.config.mjs +0 -1
- package/templates/react/base/src/assets/playcanvas.png +0 -0
- package/templates/react/base/src/inspector.css +0 -33
- package/templates/react/base/src/inspector.ts +0 -38
- package/templates/react/base/src/starter.css +0 -104
- package/templates/react/base/src/vite-env.d.ts +0 -19
- package/templates/react/interactive-sphere/index.html +0 -13
- package/templates/react/interactive-sphere/src/index.css +0 -4
- package/templates/react/interactive-sphere/src/main.tsx +0 -11
- package/templates/react/model-viewer/index.html +0 -13
- package/templates/react/model-viewer/src/index.css +0 -9
- package/templates/react/model-viewer/src/main.tsx +0 -11
- package/templates/react/spinning-cube/index.html +0 -13
- package/templates/react/spinning-cube/src/index.css +0 -9
- package/templates/react/spinning-cube/src/main.tsx +0 -11
- package/templates/react/splat-viewer/index.html +0 -13
- package/templates/react/splat-viewer/src/index.css +0 -9
- package/templates/react/splat-viewer/src/main.tsx +0 -11
- package/templates/web-components/base/.prettierignore +0 -3
- package/templates/web-components/base/_gitignore +0 -24
- package/templates/web-components/base/_npmrc +0 -1
- package/templates/web-components/base/prettier.config.mjs +0 -1
- package/templates/web-components/base/src/assets/playcanvas.png +0 -0
- package/templates/web-components/base/src/inspector.css +0 -33
- package/templates/web-components/base/src/inspector.ts +0 -38
- package/templates/web-components/base/src/starter.css +0 -104
- package/templates/web-components/base/src/vite-env.d.ts +0 -19
- package/templates/web-components/interactive-sphere/src/style.css +0 -53
- package/templates/web-components/model-viewer/src/style.css +0 -8
- package/templates/web-components/spinning-cube/src/style.css +0 -8
- package/templates/web-components/splat-viewer/src/style.css +0 -8
- /package/templates/{engine/base → _common}/.prettierignore +0 -0
- /package/templates/{engine/base → _common}/_gitignore +0 -0
- /package/templates/{engine/base → _common}/_npmrc +0 -0
- /package/templates/{engine/base → _common}/prettier.config.mjs +0 -0
- /package/templates/{engine/base → _common}/src/assets/playcanvas.png +0 -0
- /package/templates/{engine/base → _common}/src/inspector.css +0 -0
- /package/templates/{engine/base → _common}/src/inspector.ts +0 -0
- /package/templates/{engine/base → _common}/src/starter.css +0 -0
- /package/templates/{engine/base → _common}/src/vite-env.d.ts +0 -0
package/README.md
CHANGED
|
@@ -38,7 +38,7 @@ Every format uses TypeScript and includes Vite, ESLint, Prettier and a productio
|
|
|
38
38
|
|
|
39
39
|
## Starters
|
|
40
40
|
|
|
41
|
-
Choose from
|
|
41
|
+
Choose from starter kits across basics, viewers, games, tools and XR. Run the creator to browse them; `spinning-cube` is the default.
|
|
42
42
|
|
|
43
43
|

|
|
44
44
|
|
|
@@ -69,7 +69,7 @@ npm create playcanvas@latest my-game -- -f react -y
|
|
|
69
69
|
| `--yes` | `-y` | Skip the prompts and take the defaults |
|
|
70
70
|
| `--help` | `-h` | Show command help |
|
|
71
71
|
|
|
72
|
-
`--yes` takes `playcanvas-project`, the `engine` format and the `spinning-cube` starter for anything you don't pass. It never deletes files, so a non-empty target directory still needs `--overwrite`. The previous `--template` and `--boilerplate` long flags remain accepted for compatibility.
|
|
72
|
+
`--yes` takes `playcanvas-project`, the `engine` format and the `spinning-cube` starter for anything you don't pass. It never deletes files, so a non-empty target directory still needs `--overwrite`. A run without a terminal, such as one started by an AI agent or in CI, takes the same defaults, so it never waits on a prompt. The previous `--template` and `--boilerplate` long flags remain accepted for compatibility.
|
|
73
73
|
|
|
74
74
|
## Agent skills
|
|
75
75
|
|
|
@@ -79,12 +79,25 @@ Every project includes [`@playcanvas/skills`](https://github.com/playcanvas/skil
|
|
|
79
79
|
|
|
80
80
|
```bash
|
|
81
81
|
npm install
|
|
82
|
+
npm test
|
|
82
83
|
npm run build
|
|
83
84
|
node dist/index.mjs
|
|
84
85
|
```
|
|
85
86
|
|
|
86
87
|
Contributions are welcome. Please open an issue before proposing a new format, starter or another substantial change.
|
|
87
88
|
|
|
89
|
+
### Adding a starter
|
|
90
|
+
|
|
91
|
+
A project is assembled from the layers under `templates/`, in this order. A file in a later layer replaces the same file in an earlier one, and each layer's `package.json` is merged into those before it.
|
|
92
|
+
|
|
93
|
+
1. `_common/`: files every project gets
|
|
94
|
+
2. `<format>/base/`: the format's tooling and README, a default `index.html` for Engine and React, and React's `src/main.tsx`
|
|
95
|
+
3. `_shared/<starter>/`: the starter's format-independent code and assets. Its `README.md` is inserted into the base README above `## Prerequisites`
|
|
96
|
+
4. `_features/<feature>/<layer>/`: what each of the starter's `features` needs in that format, as mapped by `FEATURES` in `templates/index.ts`
|
|
97
|
+
5. `<format>/<starter>/`: the starter's scene in that format
|
|
98
|
+
|
|
99
|
+
To add a starter, list it in `STARTERS` in `templates/index.ts`, add `_shared/<starter>/README.md`, and add its scene to every format: `engine/<starter>/src/main.ts`, `react/<starter>/src/App.tsx`, and `web-components/<starter>/index.html` with `src/main.ts`. Import `./starter.css` for the shared page and panel styles, and override a base file only when the starter needs something different. `npm test` checks that the starter exists in every format, and CI scaffolds and builds it in each one.
|
|
100
|
+
|
|
88
101
|
## License
|
|
89
102
|
|
|
90
103
|
[MIT](LICENSE)
|
package/dist/index.mjs
CHANGED
|
@@ -1,57 +1,57 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{styleText as
|
|
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===
|
|
4
|
-
`){
|
|
5
|
-
`&&(n
|
|
6
|
-
`)}function
|
|
2
|
+
import{styleText as c}from"node:util";import k,{stdin as Ae,stdout as ae}from"node:process";import ke from"node:readline";import"node:tty";import w from"node:fs";import x from"node:path";import{fileURLToPath as je}from"node:url";const Oe=(()=>{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}})(),Be=e=>e===12288||e>=65281&&e<=65376||e>=65504&&e<=65510,Le=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,Me=/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]|\u001b\]8;[^;]*;.*?(?:\u0007|\u001b\u005c)/y,Pe=/[\x00-\x08\x0A-\x1F\x7F-\x9F]{1,1000}/y,oe=/(?:(?![\uFF61-\uFF9F\uFF00-\uFFEF])[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}\p{Script=Tangut}]){1,1000}/yu,Ne=/\t{1,1000}/y,le=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"),De=/(?:[\x20-\x7E\xA0-\xFF](?!\uFE0F)){1,1000}/y,We=new RegExp("\\p{M}+","gu"),Ve={limit:1/0,ellipsis:""},ce=(e,t={},r={})=>{const i=t.limit??1/0,n=t.ellipsis??"",l=t?.ellipsisWidth??(n?ce(n,Ve,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,b=[[De,h],[Me,a],[Pe,o],[Ne,s],[le,u],[oe,m]];let g=0,$=0,_=e.length,F=0,T=!1,R=_,I=Math.max(0,i-l),v=0,f=0,y=0,p=0;e:for(;;){if(f>v||$>=_&&$>g){const S=e.slice(v,f)||e.slice(g,$);F=0;for(const B of S.replaceAll(We,"")){const C=B.codePointAt(0)||0;if(Be(C)?p=d:Le(C)?p=m:p=h,y+p>I&&(R=Math.min(R,Math.max(v,g)+F)),y+p>i){T=!0;break e}F+=B.length,y+=p}v=f=0}if($>=_)break e;for(let S=0,B=b.length;S<B;S++){const[C,ne]=b[S];if(C.lastIndex=$,C.test(e)){if(F=C===oe?Oe(e.slice($,C.lastIndex)):C===le?1:C.lastIndex-$,p=F*ne,y+p>I&&(R=Math.min(R,$+Math.floor((I-y)/ne))),y+p>i){T=!0;break e}y+=p,v=g,f=$,$=g=C.lastIndex;continue e}}$+=1}return{width:T?I:y,index:T?R:_,truncated:T,ellipsed:T&&i>=l}},Ue={limit:1/0,ellipsis:"",ellipsisWidth:0},M=(e,t={})=>ce(e,Ue,t).width,V="\x1B",ue="\x9B",Ge=39,q="\x07",he="[",Ke="]",me="m",X=`${Ke}8;;`,de=new RegExp(`(?:\\${he}(?<code>\\d+)m|\\${X}(?<uri>.*)${q})`,"y"),fe=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},pe=e=>`${V}${he}${e}${me}`,ge=e=>`${V}${X}${e}${q}`,Z=(e,t,r)=>{const i=t[Symbol.iterator]();let n=!1,l=!1,a=e.at(-1),o=a===void 0?0:M(a),s=i.next(),u=i.next(),d=0;for(;!s.done;){const h=s.value,m=M(h);o+m<=r?e[e.length-1]+=h:(e.push(h),o=0),(h===V||h===ue)&&(n=!0,l=t.startsWith(X,d+1)),n?l?h===q&&(n=!1,l=!1):h===me&&(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())},He=e=>{const t=e.split(" ");let r=t.length;for(;r&&!M(t[r-1]);)r--;return r===t.length?e:t.slice(0,r).join(" ")+t.slice(r).join("")},ze=(e,t,r={})=>{if(r.trim!==!1&&e.trim()==="")return"";let i="",n,l;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 g=o.at(-1)??"",$=g.trimStart();g.length!==$.length&&(o[o.length-1]=$,s=M($))}h!==0&&(s>=t&&(r.wordWrap===!1||r.trim===!1)&&(o.push(""),s=0),(s||r.trim===!1)&&(o[o.length-1]+=" ",s++));const b=M(m);if(r.hard&&b>t){const g=t-s,$=1+Math.floor((b-g-1)/t);Math.floor((b-1)/t)<$&&o.push(""),Z(o,m,t),s=M(o.at(-1)??"");continue}if(s+b>t&&s&&b){if(r.wordWrap===!1&&s<t){Z(o,m,t),s=M(o.at(-1)??"");continue}o.push(""),s=0}if(s+b>t&&r.wordWrap===!1){Z(o,m,t),s=M(o.at(-1)??"");continue}o[o.length-1]+=m,s+=b}r.trim!==!1&&(o=o.map(h=>He(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===V||m===ue){de.lastIndex=h+1;const g=de.exec(u)?.groups;if(g?.code!==void 0){const $=Number.parseFloat(g.code);n=$===Ge?void 0:$}else g?.uri!==void 0&&(l=g.uri.length===0?void 0:g.uri)}if(u[h+1]===`
|
|
4
|
+
`){l&&(i+=ge(""));const b=n?fe(n):void 0;n&&b&&(i+=pe(b))}else m===`
|
|
5
|
+
`&&(n&&fe(n)&&(i+=pe(n)),l&&(i+=ge(l)))}return i},Je=/\r?\n/;function U(e,t,r){return String(e).normalize().split(Je).map(i=>ze(i,t,r)).join(`
|
|
6
|
+
`)}function Ye(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var Q,be;function qe(){if(be)return Q;be=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)},l={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 Q={cursor:i,scroll:n,erase:l,beep:r},Q}var O=qe();function G(e,t,r){if(!r.some(a=>!a.disabled))return e;const i=e+t,n=Math.max(r.length-1,0),l=i<0?n:i>n?0:i;return r[l]?.disabled?G(l,t<0?-1:1,r):l}const Xe=["up","down","left","right","space","enter","cancel"],P={actions:new Set(Xe),aliases:new Map([["k","up"],["j","down"],["h","left"],["l","right"],["","cancel"],["escape","cancel"]]),withGuide:!0};function Ze(e){if(e!==void 0)return e;const t=process.env.ACCESSIBLE;return t!==void 0&&t!==""&&t!=="0"&&t!=="false"}function ye(e,t){if(typeof e=="string")return P.aliases.get(e)===t;for(const r of e)if(r!==void 0&&ye(r,t))return!0;return!1}function Qe(e,t){if(e===t)return;const r=e.split(`
|
|
7
7
|
`),i=t.split(`
|
|
8
|
-
`),n=Math.max(r.length,i.length),
|
|
8
|
+
`),n=Math.max(r.length,i.length),l=[];for(let a=0;a<n;a++)r[a]!==i[a]&&l.push(a);return{lines:l,numLinesBefore:r.length,numLinesAfter:i.length,numLines:n}}globalThis.process.platform.startsWith("win");const ee=Symbol("clack:cancel");function W(e){return e===ee}function K(e,t){const r=e;r.isTTY&&r.setRawMode(t)}const $e=e=>"columns"in e&&typeof e.columns=="number"?e.columns:80,xe=e=>"rows"in e&&typeof e.rows=="number"?e.rows:20;function te(e,t,r,i=r,n=r,l){const a=$e(e??ae);return U(t,a-r.length,{hard:!0,trim:!1}).split(`
|
|
9
9
|
`).map((o,s,u)=>{const d=o;return s===0?`${i}${d}`:s===u.length-1?`${n}${d}`:`${r}${d}`}).join(`
|
|
10
|
-
`)}function
|
|
11
|
-
`),
|
|
12
|
-
`).length-1;this.output.write(
|
|
13
|
-
`);this.output.write(o[a]),this._prevFrame=t,this.output.write(
|
|
10
|
+
`)}function et(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 we{input;output;_abortSignal;rl;opts;_render;_track=!1;_prevFrame="";_subscribers=new Map;_cursor=0;state="initial";error="";value;userInput="";get accessible(){return Ze(this.opts.accessible)}constructor(t,r=!0){const{input:i=Ae,output:n=ae,render:l,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=l.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 l of i)l.cb(...r),l.once&&n.push(()=>i.splice(i.indexOf(l),1));for(const l of n)l()}prompt(){return new Promise(t=>{if(this._abortSignal){if(this._abortSignal.aborted)return this.state="cancel",this.close(),t(ee);this._abortSignal.addEventListener("abort",()=>{this.state="cancel",this.close()},{once:!0})}this.rl=ke.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),K(this.input,!0),this.output.on("resize",this.render),this.render(),this.once("submit",()=>{this.output.write(O.cursor.show),this.output.off("resize",this.render),K(this.input,!1),t(this.value)}),this.once("cancel",()=>{this.output.write(O.cursor.show),this.output.off("resize",this.render),K(this.input,!1),t(ee)})})}_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&&P.aliases.has(r.name)&&this.emit("cursor",P.aliases.get(r.name)),P.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=et(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")}ye([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
|
+
`),K(this.input,!1),this.rl?.close(),this.rl=void 0,this.emit(`${this.state}`,this.value),this.unsubscribe()}restoreCursor(){const t=U(this._prevFrame,process.stdout.columns,{hard:!0,trim:!1}).split(`
|
|
12
|
+
`).length-1;this.output.write(O.cursor.move(-999,t*-1))}render(){const t=U(this._render(this)??"",process.stdout.columns,{hard:!0,trim:!1});if(t!==this._prevFrame){if(this.state==="initial")this.output.write(O.cursor.hide);else{const r=Qe(this._prevFrame,t),i=xe(this.output);if(this.restoreCursor(),r){const n=Math.max(0,r.numLinesAfter-i),l=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(O.cursor.move(0,a-l)),this.output.write(O.erase.lines(1));const o=t.split(`
|
|
13
|
+
`);this.output.write(o[a]),this._prevFrame=t,this.output.write(O.cursor.move(0,o.length-a-1));return}else if(r.lines.length>1){if(n<l)a=n;else{const s=a-l;s>0&&this.output.write(O.cursor.move(0,s))}this.output.write(O.erase.down());const o=t.split(`
|
|
14
14
|
`).slice(a);this.output.write(o.join(`
|
|
15
|
-
`)),this._prevFrame=t;return}}this.output.write(
|
|
16
|
-
`);
|
|
17
|
-
`);if(h.length>0){const[m,...
|
|
15
|
+
`)),this._prevFrame=t;return}}this.output.write(O.erase.down())}this.output.write(t),this.state==="initial"&&(this.state="active"),this._prevFrame=t}}}let tt=class extends we{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?G(i,1,this.options):i,this.changeValue(),this.on("cursor",n=>{switch(n){case"left":case"up":this.cursor=G(this.cursor,-1,this.options);break;case"down":case"right":this.cursor=G(this.cursor,1,this.options);break}this.changeValue()})}};class rt extends we{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}${c("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 st(){return k.platform!=="win32"?k.env.TERM!=="linux":!!k.env.CI||!!k.env.WT_SESSION||!!k.env.TERMINUS_SUBLIME||k.env.ConEmuTask==="{cmd::Cmder}"||k.env.TERM_PROGRAM==="Terminus-Sublime"||k.env.TERM_PROGRAM==="vscode"||k.env.TERM==="xterm-256color"||k.env.TERM==="alacritty"||k.env.TERMINAL_EMULATOR==="JetBrains-JediTerm"}const it=st(),A=(e,t)=>it?e:t,ve=A("\u25C6","*"),nt=A("\u25A0","x"),at=A("\u25B2","x"),Se=A("\u25C7","o"),E=A("\u2502","|"),N=A("\u2514","\u2014"),ot=A("\u25CF",">"),Ee=A("\u25CB"," "),lt=A("\u25CF","\u2022"),ct=A("\u25C6","*"),ut=A("\u25B2","!"),ht=A("\u25A0","x"),_e=e=>{switch(e){case"initial":case"active":return c("cyan",ve);case"cancel":return c("red",nt);case"error":return c("yellow",at);case"submit":return c("green",Se);case"validating":return c("dim",ve)}},mt=e=>{switch(e){case"initial":case"active":return c("cyan",E);case"cancel":return c("red",E);case"error":return c("yellow",E);case"submit":return c("green",E)}};function dt(e,t){const r=[`${t?`${c("cyan",E)} `:""}${e.join(" \u2022 ")}`];return t&&r.push(c("cyan",N)),r}const Ie=(e,t,r,i,n,l=!1)=>{let a=t,o=0;if(l)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}},ft=({cursor:e,options:t,style:r,output:i=process.stdout,maxItems:n=Number.POSITIVE_INFINITY,columnPadding:l=0,rowPadding:a=4})=>{const o=$e(i)-l,s=xe(i),u=c("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 b=h<t.length&&m>0,g=h<t.length&&m+h<t.length;const $=Math.min(m+h,t.length),_=[];let F=0;b&&F++,g&&F++;const T=m+(b?1:0),R=$-(g?1:0);for(let v=T;v<R;v++){const f=t[v],y=f?r(f,v===e):"",p=U(y,o,{hard:!0,trim:!1}).split(`
|
|
16
|
+
`);_.push(p),F+=p.length}if(F>d){let v=0,f=0,y=F;const p=e-T;let S=d;const B=()=>Ie(_,y,0,p,S),C=()=>Ie(_,y,p+1,_.length,S,!0);b?({lineCount:y,removals:v}=B(),y>S&&(g||(S-=1),{lineCount:y,removals:f}=C())):(g||(S-=1),{lineCount:y,removals:f}=C(),y>S&&(S-=1,{lineCount:y,removals:v}=B())),v>0&&(b=!0,_.splice(0,v)),f>0&&(g=!0,_.splice(_.length-f,f))}const I=[];b&&I.push(u);for(const v of _)for(const f of v)I.push(f);return g&&I.push(u),I};`${c("dim","\u2191/\u2193")}`,`${c("dim","Space:")}`,`${c("dim","Enter:")}`;const L={message:(e=[],{symbol:t=c("gray",E),secondarySymbol:r=c("gray",E),output:i=process.stdout,spacing:n=1,withGuide:l}={})=>{const a=[],o=l??P.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,...b]=h;m.length>0?a.push(`${u}${m}`):a.push(o?t:"");for(const g of b)g.length>0?a.push(`${d}${g}`):a.push(o?r:"")}i.write(`${a.join(`
|
|
18
18
|
`)}
|
|
19
|
-
`)},info:(e,t)=>{
|
|
19
|
+
`)},info:(e,t)=>{L.message(e,{...t,symbol:c("blue",lt)})},success:(e,t)=>{L.message(e,{...t,symbol:c("green",ct)})},step:(e,t)=>{L.message(e,{...t,symbol:c("green",Se)})},warn:(e,t)=>{L.message(e,{...t,symbol:c("yellow",ut)})},warning:(e,t)=>{L.warn(e,t)},error:(e,t)=>{L.message(e,{...t,symbol:c("red",ht)})}},pt=(e="",t)=>{const r=process.stdout,i=`${c("gray",N)} `;r.write(`${i}${c("red",e)}
|
|
20
20
|
|
|
21
|
-
`)},
|
|
22
|
-
${
|
|
21
|
+
`)},gt=(e="",t)=>{const r=process.stdout,i=`${c("gray",E)}
|
|
22
|
+
${c("gray",N)} `;r.write(`${i}${e}
|
|
23
23
|
|
|
24
|
-
`)},
|
|
24
|
+
`)},bt=[`${c("dim","\u2191/\u2193")} to navigate`,`${c("dim","Enter:")} confirm`],H=(e,t)=>e.includes(`
|
|
25
25
|
`)?e.split(`
|
|
26
26
|
`).map(r=>t(r)).join(`
|
|
27
|
-
`):t(e),
|
|
27
|
+
`):t(e),re=e=>{const t=(i,n)=>{if(i===void 0)return"";const l=i.label??String(i.value);switch(n){case"disabled":return`${c("gray",Ee)} ${H(l,a=>c("gray",a))}${i.hint?` ${c("dim",`(${i.hint??"disabled"})`)}`:""}`;case"selected":return`${H(l,a=>c("dim",a))}`;case"active":return`${c("green",ot)} ${l}${i.hint?` ${c("dim",`(${i.hint})`)}`:""}`;case"cancelled":return`${H(l,a=>c(["strikethrough","dim"],a))}`;default:return`${c("dim",Ee)} ${H(l,a=>c("dim",a))}`}},r=e.showInstructions??!0;return new tt({options:e.options,signal:e.signal,input:e.input,output:e.output,initialValue:e.initialValue,render(){const i=e.withGuide??P.withGuide,n=`${_e(this.state)} `,l=`${mt(this.state)} `,a=te(e.output,e.message,l,n),o=`${i?`${c("gray",E)}
|
|
28
28
|
`:""}${a}
|
|
29
|
-
`;switch(this.state){case"submit":{const s=i?`${
|
|
30
|
-
${
|
|
31
|
-
`).length,d=r?
|
|
32
|
-
`),m=d.length+1;return`${o}${s}${
|
|
29
|
+
`;switch(this.state){case"submit":{const s=i?`${c("gray",E)} `:"",u=te(e.output,t(this.options[this.cursor],"selected"),s);return`${o}${u}`}case"cancel":{const s=i?`${c("gray",E)} `:"",u=te(e.output,t(this.options[this.cursor],"cancelled"),s);return`${o}${u}${i?`
|
|
30
|
+
${c("gray",E)}`:""}`}default:{const s=i?`${c("cyan",E)} `:"",u=o.split(`
|
|
31
|
+
`).length,d=r?dt(bt,i):i?[c("cyan",N)]:[],h=d.join(`
|
|
32
|
+
`),m=d.length+1;return`${o}${s}${ft({output:e.output,cursor:this.cursor,options:this.options,maxItems:e.maxItems,columnPadding:s.length,rowPadding:u+m,style:(b,g)=>t(b,b.disabled?"disabled":g?"active":"inactive")}).join(`
|
|
33
33
|
${s}`)}
|
|
34
34
|
${h}
|
|
35
|
-
`}}}}).prompt()};`${
|
|
36
|
-
`:""}${
|
|
37
|
-
`,i=e.placeholder&&e.placeholder.length>0?
|
|
35
|
+
`}}}}).prompt()};`${c("gray",E)}`;const Fe=e=>new rt({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??P.withGuide,r=`${`${t?`${c("gray",E)}
|
|
36
|
+
`:""}${_e(this.state)} `}${e.message}
|
|
37
|
+
`,i=e.placeholder&&e.placeholder.length>0?c("inverse",e.placeholder[0])+c("dim",e.placeholder.slice(1)):c(["inverse","hidden"],"_"),n=this.userInput?this.userInputWithCursor:i,l=this.value??"";switch(this.state){case"validating":{const a=t?`${c("cyan",E)} `:"",o=t?c("cyan",N):"",s=c("dim",n),u=c("dim","Validating...");return`${r}${a}${s}
|
|
38
38
|
${o} ${u}
|
|
39
|
-
`}case"error":{const a=this.error?` ${
|
|
39
|
+
`}case"error":{const a=this.error?` ${c("yellow",this.error)}`:"",o=t?`${c("yellow",E)} `:"",s=t?c("yellow",N):"";return`${r.trim()}
|
|
40
40
|
${o}${n}
|
|
41
41
|
${s}${a}
|
|
42
|
-
`}case"submit":{const a=
|
|
43
|
-
${o}`:""}`}default:{const a=t?`${
|
|
42
|
+
`}case"submit":{const a=l?`${t?" ":""}${c("dim",l)}`:"",o=t?c("gray",E):"";return`${r}${o}${a}`}case"cancel":{const a=l?` ${c(["strikethrough","dim"],l)}`:"",o=t?c("gray",E):"";return`${r}${o}${a}${l.trim()?`
|
|
43
|
+
${o}`:""}`}default:{const a=t?`${c("cyan",E)} `:"",o=t?c("cyan",N):"";return`${r}${a}${n}
|
|
44
44
|
${o}
|
|
45
|
-
`}}}}).prompt();function
|
|
45
|
+
`}}}}).prompt();function se(e){return e==null?[]:Array.isArray(e)?e:[e]}function yt(e,t,r,i){var n,l=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]=l==null?a:Array.isArray(l)?l.concat(a):[l,a]}function $t(e,t){e=e||[],t=t||{};var r,i,n,l,a,o={_:[]},s=0,u=0,d=0,h=e.length;const m=t.alias!==void 0,b=t.unknown!==void 0,g=t.default!==void 0;if(t.alias=t.alias||{},t.string=se(t.string),t.boolean=se(t.boolean),m)for(r in t.alias)for(i=t.alias[r]=se(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(g){for(r in t.default)if(l=typeof t.default[r],i=t.alias[r]=t.alias[r]||[],t[l]!==void 0)for(t[l].push(r),s=0;s<i.length;s++)t[l].push(i[s])}const $=b?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(l=n.substring(u+3),b&&!~$.indexOf(l))return t.unknown(n);o[l]=!1}else{for(d=u+1;d<n.length&&n.charCodeAt(d)!==61;d++);for(l=n.substring(u,d),a=n.substring(++d)||s+1===h||(""+e[s+1]).charCodeAt(0)===45||e[++s],i=u===2?[l]:l,d=0;d<i.length;d++){if(l=i[d],b&&!~$.indexOf(l))return t.unknown("-".repeat(u)+l);yt(o,l,d+1<i.length||a,t)}}}if(g)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:{}},Ce;function xt(){if(Ce)return z.exports;Ce=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+l(h,s,u,m)+s:o+h+s},l=(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 wt=xt();const vt=Ye(wt),{cyan:St,magenta:Et,yellow:_t}=vt,J=[{name:"engine",display:"Engine",description:"with the PlayCanvas Engine API directly",color:_t},{name:"react",display:"React",description:"with @playcanvas/react",color:St},{name:"web-components",display:"Web Components",description:"with @playcanvas/web-components",color:Et}],It={physics:{engine:["assets"],react:["react"],"web-components":["assets"]}},D=[{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:"web-lens",display:"Snapchat Web Lens",category:"Games",description:"a portrait game with Snapchat leaderboards, packaged for upload"},{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"}],Ft=async({argFormat:e,yes:t,cancel:r})=>{if(e&&J.some(n=>n.name===e))return e;if(e&&t)throw new Error(`Unknown format: ${e}. Choose from: ${J.map(n=>n.name).join(", ")}`);if(t)return J[0].name;const i=await re({message:e?`"${e}" isn't a valid format. Please choose from below: `:"Select a format:",options:J.map(n=>({label:n.color(n.display),value:n.name,hint:n.description}))});return W(i)&&r(),i},Ct=async({argStarter:e,yes:t,cancel:r})=>{if(e&&D.some(n=>n.name===e))return e;if(t){if(!e)return D[0].name;throw new Error(`Unknown starter: ${e}. Choose from: ${D.map(n=>n.name).join(", ")}`)}const i=await re({message:e?`"${e}" isn't a valid starter. Please choose from below: `:"Select a starter:",initialValue:D[0].name,options:D.map(n=>({label:n.display,value:n.name,hint:`${n.category} \xB7 ${n.description}`}))});return W(i)&&r(),i},Te=e=>/^(?:@[a-z\d\-*~][a-z\d\-*._~]*\/)?[a-z\d\-~][a-z\d\-._~]*$/.test(e),ie=e=>e.trim().toLowerCase().replace(/\s+/g,"-").replace(/^[._]/,"").replace(/[^a-z\d\-~]+/g,"-"),Tt=async({targetDir:e,yes:t,cancel:r})=>{let i=x.basename(x.resolve(e));if(!Te(i)){if(t)return ie(i);const n=await Fe({message:"Package name:",defaultValue:ie(i),placeholder:ie(i),validate:l=>{if(!l||!Te(l))return"Invalid package.json name"}});if(W(n))return r();i=n}return i},Y=e=>e.trim().replace(/\/+$/g,""),Rt=e=>{const t=w.readdirSync(e);return t.length===0||t.length===1&&t[0]===".git"},At=e=>{if(w.existsSync(e))for(const t of w.readdirSync(e))t!==".git"&&w.rmSync(x.resolve(e,t),{recursive:!0,force:!0})},kt=async({argTargetDir:e,defaultTargetDir:t,yes:r,cancel:i})=>{let n=e?Y(String(e)):void 0;if(!n&&r)return t;if(!n){const l=await Fe({message:"Project name:",defaultValue:t,placeholder:t,validate:a=>{if(a&&!Y(a))return"Invalid project name"}});W(l)&&i(),n=Y(l)}return n},jt=async({targetDir:e,argOverwrite:t,yes:r,cancel:i})=>{if(w.existsSync(e)&&!Rt(e)){if(r&&!t)throw new Error(`Target directory "${e}" is not empty. Pass --overwrite to remove its contents.`);const n=t?"yes":await re({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(W(n))return i();switch(n){case"yes":At(e);break;case"no":i();return}}},Ot=["scripts","dependencies","devDependencies","peerDependencies","engines"],Re="## Prerequisites",Bt=e=>{const{cwd:t,targetDir:r,format:i,starter:n,packageName:l,skills:a,renameFiles:o}=e,s=x.resolve(t,r);w.mkdirSync(s,{recursive:!0});const u=process.env.npm_config_user_agent?.split("/")[0]??"npm";L.step(`Creating project in ${s}...`);const d=je(import.meta.url),h=x.dirname(d),m=e.templatesDir??[x.resolve(h,"../templates"),x.resolve(h,"../../templates")].find(f=>w.existsSync(x.join(f,i)));if(!m)throw new Error(`Format directory not found for: ${i}`);const b=D.find(f=>f.name===n);if(!b)throw new Error(`Starter not found: ${n}`);const g=x.join(m,"_shared",n),$=x.join(m,i,n);if(!w.existsSync($))throw new Error(`Starter directory not found: ${i}/${n}`);const _=[x.join(m,"_common"),x.join(m,i,"base"),g,...(b.features??[]).flatMap(f=>It[f][i].map(y=>x.join(m,"_features",f,y))),$].filter(f=>w.existsSync(f)),F=x.join(g,"README.md");for(const f of _){const y=x.join(f,"package.json");w.cpSync(f,s,{recursive:!0,filter:p=>p!==y&&p!==F})}if(w.existsSync(F)){const f=x.join(s,"README.md"),y=`${w.readFileSync(F,"utf8").trimEnd()}
|
|
46
46
|
|
|
47
|
-
${
|
|
48
|
-
`),a){const
|
|
49
|
-
`;const
|
|
50
|
-
cd ${
|
|
51
|
-
yarn`,
|
|
52
|
-
yarn dev`;break;default:
|
|
53
|
-
${u} install`,
|
|
54
|
-
${u} run dev`;break}
|
|
47
|
+
${Re}`,p=w.readFileSync(f,"utf8").replace(Re,()=>y);w.writeFileSync(f,p)}for(const[f,y]of Object.entries(o)){const p=x.join(s,f);w.existsSync(p)&&w.renameSync(p,x.join(s,y))}const T=_.map(f=>x.join(f,"package.json")).filter(f=>w.existsSync(f)).map(f=>JSON.parse(w.readFileSync(f,"utf-8"))),R=Object.assign({},...T);for(const f of Ot){const y=Object.assign({},...T.map(S=>S[f])),p=Object.entries(y).sort(([S],[B])=>S>B?1:-1);p.length?R[f]=Object.fromEntries(p):delete R[f]}if(R.name=l,w.writeFileSync(x.join(s,"package.json"),JSON.stringify(R,null,4)+`
|
|
48
|
+
`),a){const f=[x.resolve(h,"../skills"),x.resolve(h,"../../skills")].find(p=>w.existsSync(p));if(!f)throw new Error("Skills directory not found");const y=w.readdirSync(f,{withFileTypes:!0}).filter(p=>p.isDirectory()).map(p=>p.name);for(const p of[".claude/skills",".agents/skills"])for(const S of y)w.cpSync(x.join(f,S),x.join(s,p,S),{recursive:!0});L.step("Added PlayCanvas agent skills (opt out with --no-skills)")}let I=`Project created. Now run:
|
|
49
|
+
`;const v=x.isAbsolute(r)?s:x.relative(t,s);switch(s!==t&&(I+=`
|
|
50
|
+
cd ${v.includes(" ")?`"${v}"`:v}`),u){case"yarn":I+=`
|
|
51
|
+
yarn`,I+=`
|
|
52
|
+
yarn dev`;break;default:I+=`
|
|
53
|
+
${u} install`,I+=`
|
|
54
|
+
${u} run dev`;break}gt(I)},j=$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"]}),Lt=process.cwd(),Mt=`Usage: npm create playcanvas@latest [OPTION]...
|
|
55
55
|
|
|
56
56
|
Create a new PlayCanvas project with TypeScript.
|
|
57
57
|
With no arguments, start the CLI in interactive mode.
|
|
@@ -63,4 +63,7 @@ Options:
|
|
|
63
63
|
--no-skills omit the PlayCanvas agent skills
|
|
64
64
|
-y, --yes skip the prompts and take the defaults
|
|
65
65
|
-h, --help show help
|
|
66
|
-
|
|
66
|
+
|
|
67
|
+
Without a terminal, as when an AI agent or CI runs it, the CLI takes the
|
|
68
|
+
defaults for any option not passed, as --yes does.
|
|
69
|
+
`,Pt={_gitignore:".gitignore",_npmrc:".npmrc"},Nt="playcanvas-project",Dt=async()=>{const e=j._[0]?Y(String(j._[0])):void 0,t=j.format??j.template,r=j.starter??j.boilerplate,i=j.overwrite,n=!!(process.stdin.isTTY&&process.stdout.isTTY),l=j.yes||!n;if(j.help){console.log(Mt);return}!n&&!j.yes&&L.info("Not running in a terminal, so using the defaults for any option not passed");const a=()=>{pt("Operation cancelled"),process.exit(0)},o=await kt({argTargetDir:e,defaultTargetDir:Nt,yes:l,cancel:a});await jt({targetDir:o,argOverwrite:i,yes:l,cancel:a});const s=await Tt({targetDir:o,yes:l,cancel:a}),u=await Ft({argFormat:t,yes:l,cancel:a}),d=await Ct({argStarter:r,yes:l,cancel:a}),h=j.skills??!0;Bt({cwd:Lt,targetDir:o,format:u,starter:d,packageName:s,skills:h,renameFiles:Pt})};Dt().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.11.
|
|
3
|
+
"version": "0.11.1",
|
|
4
4
|
"description": "Scaffold a PlayCanvas project",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"playcanvas",
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
"fmt:fix": "prettier --write .",
|
|
40
40
|
"lint": "eslint .",
|
|
41
41
|
"lint:fix": "eslint . --fix",
|
|
42
|
+
"test": "vitest run",
|
|
42
43
|
"typecheck": "tsc --noEmit"
|
|
43
44
|
},
|
|
44
45
|
"devDependencies": {
|
|
@@ -49,7 +50,9 @@
|
|
|
49
50
|
"mri": "^1.2.0",
|
|
50
51
|
"picocolors": "^1.1.1",
|
|
51
52
|
"prettier": "^3.9.10",
|
|
52
|
-
"unbuild": "^3.6.1"
|
|
53
|
+
"unbuild": "^3.6.1",
|
|
54
|
+
"vite": "^8.3.5",
|
|
55
|
+
"vitest": "^5.0.5"
|
|
53
56
|
},
|
|
54
57
|
"engines": {
|
|
55
58
|
"node": ">=22.23.2"
|
|
@@ -7,10 +7,13 @@ These files are copied from `playcanvas/engine` tag `v2.21.4` (commit
|
|
|
7
7
|
They are a WebAssembly build of [ammo.js](https://github.com/kripken/ammo.js),
|
|
8
8
|
distributed under the zlib license in `LICENSE`.
|
|
9
9
|
|
|
10
|
+
The asm.js fallback in that folder, `ammo.js`, is left out, so the starters
|
|
11
|
+
configure no fallback: every browser that runs them supports WebAssembly, and
|
|
12
|
+
Vite would copy the 2 MB file into every physics project's build.
|
|
13
|
+
|
|
10
14
|
## SHA-256
|
|
11
15
|
|
|
12
16
|
```text
|
|
13
|
-
ef166d1315bc4a6441a8de341ecdf6ac4e7d69055caec65c523ed1a4e8e19b15 ammo.js
|
|
14
17
|
5645b5a0c4f03be9d9d1ae604ffacd5e5e525310cfd1d0ed27474cdd1f34aab0 ammo.wasm.js
|
|
15
18
|
a61b504d4a6ce6bb93bd843e0f61edb8115e7317f1b3462247031a83ddb25d09 ammo.wasm.wasm
|
|
16
19
|
```
|
|
@@ -32,7 +32,7 @@ Open <http://localhost:5173>. Edit the files under `src/` and save to see the sc
|
|
|
32
32
|
| `npm run fmt` | Check formatting |
|
|
33
33
|
| `npm run typecheck` | Run TypeScript checks |
|
|
34
34
|
|
|
35
|
-
Run `npm run build` to generate a deployable static site in `dist/`.
|
|
35
|
+
Run `npm run build` to generate a deployable static site in `dist/`. Its paths are relative, so it runs from any folder, such as a GitHub Pages project site or an itch.io upload.
|
|
36
36
|
|
|
37
37
|
## Agent skills
|
|
38
38
|
|
|
@@ -2,6 +2,9 @@ import { defineConfig } from 'vite';
|
|
|
2
2
|
|
|
3
3
|
// https://vite.dev/config/
|
|
4
4
|
export default defineConfig({
|
|
5
|
+
// Relative URLs, so the build runs wherever it's served from: a domain root, a subpath such as
|
|
6
|
+
// GitHub Pages or itch.io, or a Snapchat Web Lens
|
|
7
|
+
base: './',
|
|
5
8
|
plugins: [
|
|
6
9
|
{
|
|
7
10
|
// Dev only: install the PlayCanvas Inspector before the app starts (press ` to toggle)
|
|
@@ -21,9 +21,8 @@ import { addFirstPersonController } from './controller';
|
|
|
21
21
|
import './starter.css';
|
|
22
22
|
|
|
23
23
|
WasmModule.setConfig('Ammo', {
|
|
24
|
-
glueUrl: '
|
|
25
|
-
wasmUrl: '
|
|
26
|
-
fallbackUrl: '/ammo/ammo.js'
|
|
24
|
+
glueUrl: './ammo/ammo.wasm.js',
|
|
25
|
+
wasmUrl: './ammo/ammo.wasm.wasm'
|
|
27
26
|
});
|
|
28
27
|
await new Promise<void>((resolve) => {
|
|
29
28
|
WasmModule.getInstance('Ammo', () => resolve());
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
import './style.css';
|
|
2
1
|
import { setupApp } from './App';
|
|
2
|
+
import './counter.css';
|
|
3
|
+
import './starter.css';
|
|
3
4
|
|
|
4
5
|
let count = 0;
|
|
5
6
|
|
|
6
|
-
document.
|
|
7
|
-
<canvas id="application-canvas"></canvas>
|
|
8
|
-
<p id="counter" class="counter">Click count: ${count}</p>
|
|
9
|
-
`;
|
|
7
|
+
document.body.insertAdjacentHTML('beforeend', `<p id="counter" class="counter">Click count: ${count}</p>`);
|
|
10
8
|
|
|
11
9
|
const counterElement = document.getElementById('counter')!;
|
|
12
10
|
|
|
@@ -17,10 +17,10 @@ import {
|
|
|
17
17
|
import { CameraControls } from 'playcanvas/scripts/esm/camera-controls.mjs';
|
|
18
18
|
import { ProceduralSky } from 'playcanvas/scripts/esm/sky/procedural-sky.mjs';
|
|
19
19
|
|
|
20
|
-
import './
|
|
20
|
+
import './starter.css';
|
|
21
21
|
|
|
22
|
-
// Swap this for your own model -
|
|
23
|
-
const MODEL_URL = '
|
|
22
|
+
// Swap this for your own model - files in public/ are served next to index.html, so use a ./ path
|
|
23
|
+
const MODEL_URL = './playcanvas-cube.glb';
|
|
24
24
|
|
|
25
25
|
const canvas = document.getElementById('application-canvas') as HTMLCanvasElement;
|
|
26
26
|
|
|
@@ -30,9 +30,8 @@ const SCALES: [number, number, number][] = [
|
|
|
30
30
|
];
|
|
31
31
|
|
|
32
32
|
WasmModule.setConfig('Ammo', {
|
|
33
|
-
glueUrl: '
|
|
34
|
-
wasmUrl: '
|
|
35
|
-
fallbackUrl: '/ammo/ammo.js'
|
|
33
|
+
glueUrl: './ammo/ammo.wasm.js',
|
|
34
|
+
wasmUrl: './ammo/ammo.wasm.wasm'
|
|
36
35
|
});
|
|
37
36
|
await new Promise<void>((resolve) => {
|
|
38
37
|
WasmModule.getInstance('Ammo', () => resolve());
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
} from 'playcanvas';
|
|
17
17
|
import { CameraControls } from 'playcanvas/scripts/esm/camera-controls.mjs';
|
|
18
18
|
|
|
19
|
-
import './
|
|
19
|
+
import './starter.css';
|
|
20
20
|
|
|
21
21
|
// Hosted so the template stays small. Capture or edit your own splat at https://superspl.at, then
|
|
22
22
|
// drop the .sog or .ply in public/ and point this at it instead
|
|
@@ -42,9 +42,8 @@ const PINE_LAYERS: [number, number, number][] = [
|
|
|
42
42
|
];
|
|
43
43
|
|
|
44
44
|
WasmModule.setConfig('Ammo', {
|
|
45
|
-
glueUrl: '
|
|
46
|
-
wasmUrl: '
|
|
47
|
-
fallbackUrl: '/ammo/ammo.js'
|
|
45
|
+
glueUrl: './ammo/ammo.wasm.js',
|
|
46
|
+
wasmUrl: './ammo/ammo.wasm.wasm'
|
|
48
47
|
});
|
|
49
48
|
await new Promise<void>((resolve) => {
|
|
50
49
|
WasmModule.getInstance('Ammo', () => resolve());
|
|
@@ -32,7 +32,7 @@ Open <http://localhost:5173>. Edit the files under `src/` and save to see the sc
|
|
|
32
32
|
| `npm run fmt` | Check formatting |
|
|
33
33
|
| `npm run typecheck` | Run TypeScript checks |
|
|
34
34
|
|
|
35
|
-
Run `npm run build` to generate a deployable static site in `dist/`.
|
|
35
|
+
Run `npm run build` to generate a deployable static site in `dist/`. Its paths are relative, so it runs from any folder, such as a GitHub Pages project site or an itch.io upload.
|
|
36
36
|
|
|
37
37
|
## Agent skills
|
|
38
38
|
|
|
@@ -3,6 +3,9 @@ import { defineConfig } from 'vite';
|
|
|
3
3
|
|
|
4
4
|
// https://vite.dev/config/
|
|
5
5
|
export default defineConfig({
|
|
6
|
+
// Relative URLs, so the build runs wherever it's served from: a domain root, a subpath such as
|
|
7
|
+
// GitHub Pages or itch.io, or a Snapchat Web Lens
|
|
8
|
+
base: './',
|
|
6
9
|
plugins: [
|
|
7
10
|
react(),
|
|
8
11
|
{
|
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
import { Application } from '@playcanvas/react';
|
|
2
2
|
import { useState } from 'react';
|
|
3
3
|
|
|
4
|
-
import './
|
|
4
|
+
import './counter.css';
|
|
5
5
|
import Scene from './Scene';
|
|
6
|
+
import './starter.css';
|
|
6
7
|
|
|
7
8
|
function App() {
|
|
8
9
|
const [count, setCount] = useState(0);
|
|
9
10
|
|
|
10
11
|
return (
|
|
11
12
|
<>
|
|
12
|
-
<
|
|
13
|
-
<
|
|
14
|
-
|
|
15
|
-
</Application>
|
|
16
|
-
</div>
|
|
13
|
+
<Application className="playcanvas-app">
|
|
14
|
+
<Scene onClick={() => setCount((count) => count + 1)} />
|
|
15
|
+
</Application>
|
|
17
16
|
<p className="counter">Click count: {count}</p>
|
|
18
17
|
</>
|
|
19
18
|
);
|
|
@@ -18,7 +18,7 @@ function Scene({ onClick }: SceneProps) {
|
|
|
18
18
|
const material = useMaterial({ diffuse });
|
|
19
19
|
|
|
20
20
|
// Load the environment map
|
|
21
|
-
const { asset: envMap } = useEnvAtlas('
|
|
21
|
+
const { asset: envMap } = useEnvAtlas('./environment-map.png');
|
|
22
22
|
|
|
23
23
|
// Change the mouse cursor based on the hover state
|
|
24
24
|
useEffect(() => {
|
|
@@ -5,8 +5,10 @@ import { TONEMAP_ACES2 } from 'playcanvas';
|
|
|
5
5
|
import { CameraControls } from 'playcanvas/scripts/esm/camera-controls.mjs';
|
|
6
6
|
import { ProceduralSky } from 'playcanvas/scripts/esm/sky/procedural-sky.mjs';
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
import './starter.css';
|
|
9
|
+
|
|
10
|
+
// Swap this for your own model - files in public/ are served next to index.html, so use a ./ path
|
|
11
|
+
const MODEL_URL = './playcanvas-cube.glb';
|
|
10
12
|
|
|
11
13
|
function Model() {
|
|
12
14
|
const { asset } = useModel(MODEL_URL);
|
|
@@ -3,6 +3,8 @@ import { Camera, GSplat, Script } from '@playcanvas/react/components';
|
|
|
3
3
|
import { useSplat } from '@playcanvas/react/hooks';
|
|
4
4
|
import { CameraControls } from 'playcanvas/scripts/esm/camera-controls.mjs';
|
|
5
5
|
|
|
6
|
+
import './starter.css';
|
|
7
|
+
|
|
6
8
|
// Hosted so the template stays small. Capture or edit your own splat at https://superspl.at, then
|
|
7
9
|
// drop the .sog or .ply in public/ and point this at it instead
|
|
8
10
|
const SPLAT_URL = 'https://developer.playcanvas.com/assets/toy-cat.sog';
|