create-playcanvas 0.3.0 → 0.5.0
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 +42 -25
- package/dist/index.mjs +49 -46
- package/package.json +3 -1
- package/skills/add-effects/SKILL.md +81 -0
- package/skills/apply-conventions/SKILL.md +73 -0
- package/skills/assemble-scene/SKILL.md +53 -0
- package/skills/assemble-scene/references/direct-engine.md +16 -0
- package/skills/assemble-scene/references/react.md +24 -0
- package/skills/assemble-scene/references/web-components.md +19 -0
- package/skills/build-app/SKILL.md +41 -0
- package/skills/build-app/references/direct-engine.md +12 -0
- package/skills/build-app/references/react.md +20 -0
- package/skills/build-app/references/web-components.md +55 -0
- package/skills/build-hud/SKILL.md +36 -0
- package/skills/calibrate-model/SKILL.md +65 -0
- package/skills/calibrate-model/references/direct-engine.md +22 -0
- package/skills/calibrate-model/references/react.md +27 -0
- package/skills/calibrate-model/references/web-components.md +19 -0
- package/skills/configure-animation/SKILL.md +50 -0
- package/skills/configure-animation/references/direct-engine.md +32 -0
- package/skills/configure-animation/references/react.md +32 -0
- package/skills/configure-animation/references/web-components.md +13 -0
- package/skills/find-examples/SKILL.md +79 -0
- package/skills/find-examples/references/direct-engine.md +15 -0
- package/skills/find-examples/references/react.md +17 -0
- package/skills/find-examples/references/web-components.md +17 -0
- package/skills/inspect-glb/SKILL.md +72 -0
- package/skills/inspect-glb/scripts/inspect.mjs +239 -0
- package/skills/light-scene/SKILL.md +84 -0
- package/skills/manage-game-state/SKILL.md +48 -0
- package/skills/reuse-scripts/SKILL.md +59 -0
- package/skills/reuse-scripts/references/direct-engine.md +41 -0
- package/skills/reuse-scripts/references/react.md +28 -0
- package/skills/reuse-scripts/references/web-components.md +27 -0
- package/templates/_shared/model-viewer/public/playcanvas-cube.glb +0 -0
- package/templates/engine/base/.prettierignore +2 -0
- package/templates/{vanilla-ts → engine/base}/README.md +7 -19
- package/templates/engine/base/eslint.config.mjs +3 -0
- package/templates/{vanilla-ts → engine/base}/src/vite-env.d.ts +6 -0
- package/templates/{vanilla-ts → engine/interactive-sphere}/src/main.ts +1 -1
- package/templates/{vanilla-ts → engine/interactive-sphere}/src/style.css +1 -1
- package/templates/{vanilla-ts → engine/interactive-sphere}/src/utils.ts +0 -1
- package/templates/engine/model-viewer/index.html +13 -0
- package/templates/engine/model-viewer/src/main.ts +81 -0
- package/templates/engine/model-viewer/src/style.css +8 -0
- package/templates/engine/spinning-cube/index.html +13 -0
- package/templates/engine/spinning-cube/src/main.ts +65 -0
- package/templates/engine/spinning-cube/src/style.css +8 -0
- package/templates/engine/splat-viewer/index.html +13 -0
- package/templates/engine/splat-viewer/src/main.ts +74 -0
- package/templates/engine/splat-viewer/src/style.css +8 -0
- package/templates/index.ts +37 -5
- package/templates/react/base/.prettierignore +2 -0
- package/templates/{react-ts → react/base}/README.md +6 -19
- package/templates/{react-ts → react/base}/eslint.config.mjs +1 -0
- package/templates/{react-ts → react/base}/src/vite-env.d.ts +6 -0
- package/templates/{react-ts → react/interactive-sphere}/src/Scene.tsx +1 -2
- package/templates/react/model-viewer/index.html +13 -0
- package/templates/react/model-viewer/src/App.tsx +43 -0
- package/templates/react/model-viewer/src/index.css +9 -0
- package/templates/react/model-viewer/src/main.tsx +11 -0
- package/templates/react/spinning-cube/index.html +13 -0
- package/templates/react/spinning-cube/src/App.tsx +36 -0
- package/templates/react/spinning-cube/src/index.css +9 -0
- package/templates/react/spinning-cube/src/main.tsx +11 -0
- package/templates/react/splat-viewer/index.html +13 -0
- package/templates/react/splat-viewer/src/App.tsx +39 -0
- package/templates/react/splat-viewer/src/index.css +9 -0
- package/templates/react/splat-viewer/src/main.tsx +11 -0
- package/templates/web-components/base/.prettierignore +2 -0
- package/templates/web-components/base/README.md +70 -0
- package/templates/web-components/base/_gitignore +24 -0
- package/templates/web-components/base/eslint.config.mjs +3 -0
- package/templates/web-components/base/package.json +31 -0
- package/templates/web-components/base/prettier.config.mjs +1 -0
- package/templates/web-components/base/src/assets/playcanvas.png +0 -0
- package/templates/web-components/base/src/vite-env.d.ts +19 -0
- package/templates/web-components/base/tsconfig.json +25 -0
- package/templates/web-components/interactive-sphere/index.html +53 -0
- package/templates/web-components/interactive-sphere/src/assets/typescript.svg +1 -0
- package/templates/web-components/interactive-sphere/src/main.ts +76 -0
- package/templates/web-components/interactive-sphere/src/style.css +111 -0
- package/templates/web-components/model-viewer/index.html +34 -0
- package/templates/web-components/model-viewer/src/main.ts +26 -0
- package/templates/web-components/model-viewer/src/style.css +8 -0
- package/templates/web-components/spinning-cube/index.html +29 -0
- package/templates/web-components/spinning-cube/src/main.ts +20 -0
- package/templates/web-components/spinning-cube/src/style.css +8 -0
- package/templates/web-components/splat-viewer/index.html +31 -0
- package/templates/web-components/splat-viewer/src/main.ts +14 -0
- package/templates/web-components/splat-viewer/src/style.css +8 -0
- package/templates/vanilla-ts/eslint.config.mjs +0 -3
- package/templates/vanilla-ts/public/environment-map.png +0 -0
- /package/templates/{react-ts → _shared/interactive-sphere}/public/environment-map.png +0 -0
- /package/templates/{react-ts → engine/base}/_gitignore +0 -0
- /package/templates/{vanilla-ts → engine/base}/package.json +0 -0
- /package/templates/{react-ts → engine/base}/prettier.config.mjs +0 -0
- /package/templates/{react-ts → engine/base}/src/assets/playcanvas.png +0 -0
- /package/templates/{vanilla-ts → engine/base}/tsconfig.json +0 -0
- /package/templates/{vanilla-ts → engine/interactive-sphere}/index.html +0 -0
- /package/templates/{vanilla-ts → engine/interactive-sphere}/src/App.ts +0 -0
- /package/templates/{vanilla-ts → engine/interactive-sphere}/src/assets/typescript.svg +0 -0
- /package/templates/{vanilla-ts → react/base}/_gitignore +0 -0
- /package/templates/{react-ts → react/base}/package.json +0 -0
- /package/templates/{vanilla-ts → react/base}/prettier.config.mjs +0 -0
- /package/templates/{vanilla-ts → react/base}/src/assets/playcanvas.png +0 -0
- /package/templates/{react-ts → react/base}/tsconfig.json +0 -0
- /package/templates/{react-ts → react/base}/vite.config.ts +0 -0
- /package/templates/{react-ts → react/interactive-sphere}/index.html +0 -0
- /package/templates/{react-ts → react/interactive-sphere}/src/App.css +0 -0
- /package/templates/{react-ts → react/interactive-sphere}/src/App.tsx +0 -0
- /package/templates/{react-ts → react/interactive-sphere}/src/assets/react.svg +0 -0
- /package/templates/{react-ts → react/interactive-sphere}/src/index.css +0 -0
- /package/templates/{react-ts → react/interactive-sphere}/src/main.tsx +0 -0
package/README.md
CHANGED
|
@@ -9,51 +9,68 @@
|
|
|
9
9
|
|
|
10
10
|
| [Engine Manual](https://developer.playcanvas.com/user-manual/engine/) | [React Manual](https://developer.playcanvas.com/user-manual/react/) | [Examples](https://playcanvas.github.io/) | [Forum](https://forum.playcanvas.com/) |
|
|
11
11
|
|
|
12
|
-
Scaffold a Vite-powered PlayCanvas project with TypeScript. Choose the PlayCanvas Engine directly
|
|
12
|
+
Scaffold a Vite-powered PlayCanvas project with TypeScript. Choose the PlayCanvas Engine directly, [`@playcanvas/react`](https://github.com/playcanvas/react) or [`@playcanvas/web-components`](https://github.com/playcanvas/web-components).
|
|
13
13
|
|
|
14
14
|
## Getting Started
|
|
15
15
|
|
|
16
|
-
Requires Node.js 22.23.2 or later.
|
|
17
|
-
|
|
18
16
|
```bash
|
|
17
|
+
# npm
|
|
19
18
|
npm create playcanvas@latest
|
|
20
|
-
|
|
21
|
-
npm install
|
|
22
|
-
npm run dev
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
The interactive setup asks for a project name, handles an existing target directory, sets the package name and lets you choose a template.
|
|
26
|
-
|
|
27
|
-
You can also use another package manager:
|
|
28
|
-
|
|
29
|
-
```bash
|
|
19
|
+
# pnpm
|
|
30
20
|
pnpm create playcanvas@latest
|
|
21
|
+
# yarn
|
|
31
22
|
yarn create playcanvas
|
|
23
|
+
# bun
|
|
32
24
|
bun x create-playcanvas@latest
|
|
33
25
|
```
|
|
34
26
|
|
|
27
|
+
Then follow the prompts. Requires Node.js 22.23.2 or later.
|
|
28
|
+
|
|
35
29
|
## Templates
|
|
36
30
|
|
|
37
|
-
| Template
|
|
38
|
-
|
|
|
39
|
-
| `
|
|
40
|
-
| `react
|
|
31
|
+
| Template | Description |
|
|
32
|
+
| ---------------- | -------------------------------------------- |
|
|
33
|
+
| `engine` | The PlayCanvas Engine API directly |
|
|
34
|
+
| `react` | `@playcanvas/react` components |
|
|
35
|
+
| `web-components` | `@playcanvas/web-components` custom elements |
|
|
41
36
|
|
|
42
|
-
|
|
37
|
+
Every template is TypeScript, and includes Vite, ESLint, Prettier and a production build.
|
|
38
|
+
|
|
39
|
+
## Boilerplates
|
|
40
|
+
|
|
41
|
+
Each template offers a choice of starting scene. `spinning-cube` is the default.
|
|
42
|
+
|
|
43
|
+
| Boilerplate | Description |
|
|
44
|
+
| -------------------- | ---------------------------------------------- |
|
|
45
|
+
| `spinning-cube` | A rotating cube and a light |
|
|
46
|
+
| `interactive-sphere` | Orbit controls, a grid and pointer picking |
|
|
47
|
+
| `model-viewer` | A glTF model, orbit controls, a procedural sky |
|
|
48
|
+
| `splat-viewer` | A Gaussian splat and orbit controls |
|
|
49
|
+
|
|
50
|
+
A template is a `base` directory of shared tooling plus one directory per boilerplate, which is copied over the top. Assets a boilerplate needs in every template live once in `templates/_shared/<boilerplate>` and are seeded in first.
|
|
43
51
|
|
|
44
52
|
## CLI Options
|
|
45
53
|
|
|
46
|
-
Pass a project name and
|
|
54
|
+
Pass a project name, template and boilerplate to skip the prompts:
|
|
47
55
|
|
|
48
56
|
```bash
|
|
49
|
-
npm create playcanvas@latest my-game -- --template react-
|
|
57
|
+
npm create playcanvas@latest my-game -- --template react --boilerplate spinning-cube
|
|
50
58
|
```
|
|
51
59
|
|
|
52
|
-
| Option
|
|
53
|
-
|
|
|
54
|
-
| `--template <name>`
|
|
55
|
-
| `--
|
|
56
|
-
| `--
|
|
60
|
+
| Option | Shorthand | Description |
|
|
61
|
+
| ---------------------- | --------- | ------------------------------------------------------- |
|
|
62
|
+
| `--template <name>` | `-t` | Use a template from the list above |
|
|
63
|
+
| `--boilerplate <name>` | `-b` | Use a boilerplate from the list above |
|
|
64
|
+
| `--overwrite` | | Remove existing files from a non-empty target directory |
|
|
65
|
+
| `--no-skills` | | Omit the PlayCanvas agent skills (included by default) |
|
|
66
|
+
| `--yes` | `-y` | Skip the prompts and take the defaults |
|
|
67
|
+
| `--help` | `-h` | Show command help |
|
|
68
|
+
|
|
69
|
+
`--yes` takes `playcanvas-project`, the `engine` template and the `spinning-cube` boilerplate for anything you don't pass. It never deletes files, so a non-empty target directory still needs `--overwrite`.
|
|
70
|
+
|
|
71
|
+
## Agent skills
|
|
72
|
+
|
|
73
|
+
Every project includes [`@playcanvas/skills`](https://github.com/playcanvas/skills) so AI coding agents such as Claude Code, Codex and Cursor pick up PlayCanvas-specific workflows automatically, with no install step. They are copied into `.claude/skills/` and `.agents/skills/`. Pass `--no-skills` to leave them out.
|
|
57
74
|
|
|
58
75
|
## Development
|
|
59
76
|
|
package/dist/index.mjs
CHANGED
|
@@ -1,59 +1,62 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{styleText as
|
|
3
|
-
`);let
|
|
4
|
-
`){l&&(
|
|
5
|
-
`&&(
|
|
6
|
-
`)}function
|
|
7
|
-
`),
|
|
8
|
-
`),
|
|
9
|
-
`).map((
|
|
10
|
-
`)}function
|
|
11
|
-
`),
|
|
12
|
-
`).length-1;this.output.write(
|
|
13
|
-
`);this.output.write(a
|
|
14
|
-
`).slice(
|
|
15
|
-
`)),this._prevFrame=
|
|
16
|
-
`);
|
|
17
|
-
`);if(h.length>0){const[
|
|
2
|
+
import{styleText as u}from"node:util";import A,{stdin as Ce,stdout as ae}from"node:process";import Re from"node:readline";import"node:tty";import _ from"node:fs";import y from"node:path";import{fileURLToPath as ke}from"node:url";const Ae=(()=>{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,Oe=/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]|\u001b\]8;[^;]*;.*?(?:\u0007|\u001b\u005c)/y,Me=/[\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"),Pe=/(?:[\x20-\x7E\xA0-\xFF](?!\uFE0F)){1,1000}/y,je=new RegExp("\\p{M}+","gu"),De={limit:1/0,ellipsis:""},ce=(e,t={},r={})=>{const s=t.limit??1/0,n=t.ellipsis??"",l=t?.ellipsisWidth??(n?ce(n,De,r).width:0),a=0,o=r.controlWidth??0,i=r.tabWidth??8,c=r.emojiWidth??2,d=2,h=r.regularWidth??1,m=r.wideWidth??d,f=[[Pe,h],[Oe,a],[Me,o],[Ne,i],[le,c],[oe,m]];let p=0,g=0,S=e.length,w=0,T=!1,v=S,I=Math.max(0,s-l),b=0,E=0,x=0,F=0;e:for(;;){if(E>b||g>=S&&g>p){const C=e.slice(b,E)||e.slice(p,g);w=0;for(const N of C.replaceAll(je,"")){const R=N.codePointAt(0)||0;if(Be(R)?F=d:Le(R)?F=m:F=h,x+F>I&&(v=Math.min(v,Math.max(b,p)+w)),x+F>s){T=!0;break e}w+=N.length,x+=F}b=E=0}if(g>=S)break e;for(let C=0,N=f.length;C<N;C++){const[R,ne]=f[C];if(R.lastIndex=g,R.test(e)){if(w=R===oe?Ae(e.slice(g,R.lastIndex)):R===le?1:R.lastIndex-g,F=w*ne,x+F>I&&(v=Math.min(v,g+Math.floor((I-x)/ne))),x+F>s){T=!0;break e}x+=F,b=p,E=g,g=p=R.lastIndex;continue e}}g+=1}return{width:T?I:x,index:T?v:S,truncated:T,ellipsed:T&&s>=l}},We={limit:1/0,ellipsis:"",ellipsisWidth:0},L=(e,t={})=>ce(e,We,t).width,V="\x1B",ue="\x9B",Ve=39,Y="\x07",he="[",Ue="]",me="m",q=`${Ue}8;;`,de=new RegExp(`(?:\\${he}(?<code>\\d+)m|\\${q}(?<uri>.*)${Y})`,"y"),pe=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},fe=e=>`${V}${he}${e}${me}`,ge=e=>`${V}${q}${e}${Y}`,X=(e,t,r)=>{const s=t[Symbol.iterator]();let n=!1,l=!1,a=e.at(-1),o=a===void 0?0:L(a),i=s.next(),c=s.next(),d=0;for(;!i.done;){const h=i.value,m=L(h);o+m<=r?e[e.length-1]+=h:(e.push(h),o=0),(h===V||h===ue)&&(n=!0,l=t.startsWith(q,d+1)),n?l?h===Y&&(n=!1,l=!1):h===me&&(n=!1):(o+=m,o===r&&!c.done&&(e.push(""),o=0)),i=c,c=s.next(),d+=h.length}a=e.at(-1),!o&&a!==void 0&&a.length&&e.length>1&&(e[e.length-2]+=e.pop())},Ge=e=>{const t=e.split(" ");let r=t.length;for(;r&&!L(t[r-1]);)r--;return r===t.length?e:t.slice(0,r).join(" ")+t.slice(r).join("")},Ke=(e,t,r={})=>{if(r.trim!==!1&&e.trim()==="")return"";let s="",n,l;const a=e.split(" ");let o=[""],i=0;for(let h=0;h<a.length;h++){const m=a[h];if(r.trim!==!1){const p=o.at(-1)??"",g=p.trimStart();p.length!==g.length&&(o[o.length-1]=g,i=L(g))}h!==0&&(i>=t&&(r.wordWrap===!1||r.trim===!1)&&(o.push(""),i=0),(i||r.trim===!1)&&(o[o.length-1]+=" ",i++));const f=L(m);if(r.hard&&f>t){const p=t-i,g=1+Math.floor((f-p-1)/t);Math.floor((f-1)/t)<g&&o.push(""),X(o,m,t),i=L(o.at(-1)??"");continue}if(i+f>t&&i&&f){if(r.wordWrap===!1&&i<t){X(o,m,t),i=L(o.at(-1)??"");continue}o.push(""),i=0}if(i+f>t&&r.wordWrap===!1){X(o,m,t),i=L(o.at(-1)??"");continue}o[o.length-1]+=m,i+=f}r.trim!==!1&&(o=o.map(h=>Ge(h)));const c=o.join(`
|
|
3
|
+
`);let d=!1;for(let h=0;h<c.length;h++){const m=c[h];if(s+=m,d)d=!1;else if(d=m>="\uD800"&&m<="\uDBFF",d)continue;if(m===V||m===ue){de.lastIndex=h+1;const p=de.exec(c)?.groups;if(p?.code!==void 0){const g=Number.parseFloat(p.code);n=g===Ve?void 0:g}else p?.uri!==void 0&&(l=p.uri.length===0?void 0:p.uri)}if(c[h+1]===`
|
|
4
|
+
`){l&&(s+=ge(""));const f=n?pe(n):void 0;n&&f&&(s+=fe(f))}else m===`
|
|
5
|
+
`&&(n&&pe(n)&&(s+=fe(n)),l&&(s+=ge(l)))}return s},He=/\r?\n/;function U(e,t,r){return String(e).normalize().split(He).map(s=>Ke(s,t,r)).join(`
|
|
6
|
+
`)}function ze(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var Z,be;function Je(){if(be)return Z;be=1;const e="\x1B",t=`${e}[`,r="\x07",s={to(a,o){return o?`${t}${o+1};${a+1}H`:`${t}${a+1}G`},move(a,o){let i="";return a<0?i+=`${t}${-a}D`:a>0&&(i+=`${t}${a}C`),o<0?i+=`${t}${-o}A`:o>0&&(i+=`${t}${o}B`),i},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 i=0;i<a;i++)o+=this.line+(i<a-1?s.up():"");return a&&(o+=s.left),o}};return Z={cursor:s,scroll:n,erase:l,beep:r},Z}var B=Je();function G(e,t,r){if(!r.some(a=>!a.disabled))return e;const s=e+t,n=Math.max(r.length-1,0),l=s<0?n:s>n?0:s;return r[l]?.disabled?G(l,t<0?-1:1,r):l}const Ye=["up","down","left","right","space","enter","cancel"],P={actions:new Set(Ye),aliases:new Map([["k","up"],["j","down"],["h","left"],["l","right"],["","cancel"],["escape","cancel"]]),withGuide:!0};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
|
+
`),s=t.split(`
|
|
8
|
+
`),n=Math.max(r.length,s.length),l=[];for(let a=0;a<n;a++)r[a]!==s[a]&&l.push(a);return{lines:l,numLinesBefore:r.length,numLinesAfter:s.length,numLines:n}}globalThis.process.platform.startsWith("win");const Q=Symbol("clack:cancel");function D(e){return e===Q}function K(e,t){const r=e;r.isTTY&&r.setRawMode(t)}const xe=e=>"columns"in e&&typeof e.columns=="number"?e.columns:80,$e=e=>"rows"in e&&typeof e.rows=="number"?e.rows:20;function ee(e,t,r,s=r,n=r,l){const a=xe(e??ae);return U(t,a-r.length,{hard:!0,trim:!1}).split(`
|
|
9
|
+
`).map((o,i,c)=>{const d=o;return i===0?`${s}${d}`:i===c.length-1?`${n}${d}`:`${r}${d}`}).join(`
|
|
10
|
+
`)}function Xe(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 we{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:s=Ce,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=s,this.output=n}unsubscribe(){this._subscribers.clear()}setSubscriber(t,r){const s=this._subscribers.get(t)??[];s.push(r),this._subscribers.set(t,s)}on(t,r){this.setSubscriber(t,{cb:r})}once(t,r){this.setSubscriber(t,{cb:r,once:!0})}emit(t,...r){const s=this._subscribers.get(t)??[],n=[];for(const l of s)l.cb(...r),l.once&&n.push(()=>s.splice(s.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(Q);this._abortSignal.addEventListener("abort",()=>{this.state="cancel",this.close()},{once:!0})}this.rl=Re.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(B.cursor.show),this.output.off("resize",this.render),K(this.input,!1),t(this.value)}),this.once("cancel",()=>{this.output.write(B.cursor.show),this.output.off("resize",this.render),K(this.input,!1),t(Q)})})}_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&&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 s=Xe(this.opts.validate,this.value);s&&(this.error=s instanceof Error?s.message:s,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(B.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(B.cursor.hide);else{const r=qe(this._prevFrame,t),s=$e(this.output);if(this.restoreCursor(),r){const n=Math.max(0,r.numLinesAfter-s),l=Math.max(0,r.numLinesBefore-s);let a=r.lines.find(o=>o>=n);if(a===void 0){this._prevFrame=t;return}if(r.lines.length===1){this.output.write(B.cursor.move(0,a-l)),this.output.write(B.erase.lines(1));const o=t.split(`
|
|
13
|
+
`);this.output.write(o[a]),this._prevFrame=t,this.output.write(B.cursor.move(0,o.length-a-1));return}else if(r.lines.length>1){if(n<l)a=n;else{const i=a-l;i>0&&this.output.write(B.cursor.move(0,i))}this.output.write(B.erase.down());const o=t.split(`
|
|
14
|
+
`).slice(a);this.output.write(o.join(`
|
|
15
|
+
`)),this._prevFrame=t;return}}this.output.write(B.erase.down())}this.output.write(t),this.state==="initial"&&(this.state="active"),this._prevFrame=t}}}let Ze=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),s=r===-1?0:r;this.cursor=this.options[s]?.disabled?G(s,1,this.options):s,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()})}},Qe=class 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),s=t.slice(this.cursor,this.cursor+1),n=t.slice(this.cursor+1);return`${r}${u("inverse",s)}${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 et(){return A.platform!=="win32"?A.env.TERM!=="linux":!!A.env.CI||!!A.env.WT_SESSION||!!A.env.TERMINUS_SUBLIME||A.env.ConEmuTask==="{cmd::Cmder}"||A.env.TERM_PROGRAM==="Terminus-Sublime"||A.env.TERM_PROGRAM==="vscode"||A.env.TERM==="xterm-256color"||A.env.TERM==="alacritty"||A.env.TERMINAL_EMULATOR==="JetBrains-JediTerm"}const tt=et(),k=(e,t)=>tt?e:t,rt=k("\u25C6","*"),it=k("\u25A0","x"),st=k("\u25B2","x"),ve=k("\u25C7","o"),$=k("\u2502","|"),j=k("\u2514","\u2014"),nt=k("\u25CF",">"),Ee=k("\u25CB"," "),at=k("\u25CF","\u2022"),ot=k("\u25C6","*"),lt=k("\u25B2","!"),ct=k("\u25A0","x"),_e=e=>{switch(e){case"initial":case"active":return u("cyan",rt);case"cancel":return u("red",it);case"error":return u("yellow",st);case"submit":return u("green",ve)}},ut=e=>{switch(e){case"initial":case"active":return u("cyan",$);case"cancel":return u("red",$);case"error":return u("yellow",$);case"submit":return u("green",$)}};function ht(e,t){const r=[`${t?`${u("cyan",$)} `:""}${e.join(" \u2022 ")}`];return t&&r.push(u("cyan",j)),r}const Se=(e,t,r,s,n,l=!1)=>{let a=t,o=0;if(l)for(let i=s-1;i>=r;i--){const c=e[i];if(c&&(a-=c.length),o++,a<=n)break}else for(let i=r;i<s;i++){const c=e[i];if(c&&(a-=c.length),o++,a<=n)break}return{lineCount:a,removals:o}},mt=({cursor:e,options:t,style:r,output:s=process.stdout,maxItems:n=Number.POSITIVE_INFINITY,columnPadding:l=0,rowPadding:a=4})=>{const o=xe(s)-l,i=$e(s),c=u("dim","..."),d=Math.max(i-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 f=h<t.length&&m>0,p=h<t.length&&m+h<t.length;const g=Math.min(m+h,t.length),S=[];let w=0;f&&w++,p&&w++;const T=m+(f?1:0),v=g-(p?1:0);for(let b=T;b<v;b++){const E=t[b],x=E?r(E,b===e):"",F=U(x,o,{hard:!0,trim:!1}).split(`
|
|
16
|
+
`);S.push(F),w+=F.length}if(w>d){let b=0,E=0,x=w;const F=e-T;let C=d;const N=()=>Se(S,x,0,F,C),R=()=>Se(S,x,F+1,S.length,C,!0);f?({lineCount:x,removals:b}=N(),x>C&&(p||(C-=1),{lineCount:x,removals:E}=R())):(p||(C-=1),{lineCount:x,removals:E}=R(),x>C&&(C-=1,{lineCount:x,removals:b}=N())),b>0&&(f=!0,S.splice(0,b)),E>0&&(p=!0,S.splice(S.length-E,E))}const I=[];f&&I.push(c);for(const b of S)for(const E of b)I.push(E);return p&&I.push(c),I};`${u("dim","\u2191/\u2193")}`,`${u("dim","Space:")}`,`${u("dim","Enter:")}`;const O={message:(e=[],{symbol:t=u("gray",$),secondarySymbol:r=u("gray",$),output:s=process.stdout,spacing:n=1,withGuide:l}={})=>{const a=[],o=l??P.withGuide,i=o?r:"",c=o?`${t} `:"",d=o?`${r} `:"";for(let m=0;m<n;m++)a.push(i);const h=Array.isArray(e)?e:e.split(`
|
|
17
|
+
`);if(h.length>0){const[m,...f]=h;m.length>0?a.push(`${c}${m}`):a.push(o?t:"");for(const p of f)p.length>0?a.push(`${d}${p}`):a.push(o?r:"")}s.write(`${a.join(`
|
|
18
18
|
`)}
|
|
19
|
-
`)},info:(t
|
|
19
|
+
`)},info:(e,t)=>{O.message(e,{...t,symbol:u("blue",at)})},success:(e,t)=>{O.message(e,{...t,symbol:u("green",ot)})},step:(e,t)=>{O.message(e,{...t,symbol:u("green",ve)})},warn:(e,t)=>{O.message(e,{...t,symbol:u("yellow",lt)})},warning:(e,t)=>{O.warn(e,t)},error:(e,t)=>{O.message(e,{...t,symbol:u("red",ct)})}},dt=(e="",t)=>{const r=process.stdout,s=`${u("gray",j)} `;r.write(`${s}${u("red",e)}
|
|
20
20
|
|
|
21
|
-
`)},
|
|
22
|
-
${
|
|
21
|
+
`)},pt=(e="",t)=>{const r=process.stdout,s=`${u("gray",$)}
|
|
22
|
+
${u("gray",j)} `;r.write(`${s}${e}
|
|
23
23
|
|
|
24
|
-
`)},
|
|
25
|
-
`)?
|
|
26
|
-
`).map(r=>
|
|
27
|
-
`):e
|
|
28
|
-
`:""}${
|
|
29
|
-
`;switch(this.state){case"submit":{const s
|
|
30
|
-
${
|
|
31
|
-
`).length,
|
|
32
|
-
`),d
|
|
33
|
-
${
|
|
24
|
+
`)},ft=[`${u("dim","\u2191/\u2193")} to navigate`,`${u("dim","Enter:")} confirm`],H=(e,t)=>e.includes(`
|
|
25
|
+
`)?e.split(`
|
|
26
|
+
`).map(r=>t(r)).join(`
|
|
27
|
+
`):t(e),te=e=>{const t=(s,n)=>{if(s===void 0)return"";const l=s.label??String(s.value);switch(n){case"disabled":return`${u("gray",Ee)} ${H(l,a=>u("gray",a))}${s.hint?` ${u("dim",`(${s.hint??"disabled"})`)}`:""}`;case"selected":return`${H(l,a=>u("dim",a))}`;case"active":return`${u("green",nt)} ${l}${s.hint?` ${u("dim",`(${s.hint})`)}`:""}`;case"cancelled":return`${H(l,a=>u(["strikethrough","dim"],a))}`;default:return`${u("dim",Ee)} ${H(l,a=>u("dim",a))}`}},r=e.showInstructions??!0;return new Ze({options:e.options,signal:e.signal,input:e.input,output:e.output,initialValue:e.initialValue,render(){const s=e.withGuide??P.withGuide,n=`${_e(this.state)} `,l=`${ut(this.state)} `,a=ee(e.output,e.message,l,n),o=`${s?`${u("gray",$)}
|
|
28
|
+
`:""}${a}
|
|
29
|
+
`;switch(this.state){case"submit":{const i=s?`${u("gray",$)} `:"",c=ee(e.output,t(this.options[this.cursor],"selected"),i);return`${o}${c}`}case"cancel":{const i=s?`${u("gray",$)} `:"",c=ee(e.output,t(this.options[this.cursor],"cancelled"),i);return`${o}${c}${s?`
|
|
30
|
+
${u("gray",$)}`:""}`}default:{const i=s?`${u("cyan",$)} `:"",c=o.split(`
|
|
31
|
+
`).length,d=r?ht(ft,s):s?[u("cyan",j)]:[],h=d.join(`
|
|
32
|
+
`),m=d.length+1;return`${o}${i}${mt({output:e.output,cursor:this.cursor,options:this.options,maxItems:e.maxItems,columnPadding:i.length,rowPadding:c+m,style:(f,p)=>t(f,f.disabled?"disabled":p?"active":"inactive")}).join(`
|
|
33
|
+
${i}`)}
|
|
34
34
|
${h}
|
|
35
|
-
`}}}}).prompt()};`${
|
|
36
|
-
`:""}${
|
|
37
|
-
`,
|
|
38
|
-
${
|
|
39
|
-
${
|
|
40
|
-
`}case"submit":{const
|
|
41
|
-
${
|
|
42
|
-
${
|
|
43
|
-
`}}}}).prompt();function
|
|
44
|
-
`);let
|
|
45
|
-
`;const
|
|
46
|
-
cd ${
|
|
47
|
-
yarn`,
|
|
48
|
-
yarn dev`;break;default:
|
|
49
|
-
${
|
|
50
|
-
${
|
|
35
|
+
`}}}}).prompt()};`${u("gray",$)}`;const Ie=e=>new Qe({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?`${u("gray",$)}
|
|
36
|
+
`:""}${_e(this.state)} `}${e.message}
|
|
37
|
+
`,s=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:s,l=this.value??"";switch(this.state){case"error":{const a=this.error?` ${u("yellow",this.error)}`:"",o=t?`${u("yellow",$)} `:"",i=t?u("yellow",j):"";return`${r.trim()}
|
|
38
|
+
${o}${n}
|
|
39
|
+
${i}${a}
|
|
40
|
+
`}case"submit":{const a=l?` ${u("dim",l)}`:"",o=t?u("gray",$):"";return`${r}${o}${a}`}case"cancel":{const a=l?` ${u(["strikethrough","dim"],l)}`:"",o=t?u("gray",$):"";return`${r}${o}${a}${l.trim()?`
|
|
41
|
+
${o}`:""}`}default:{const a=t?`${u("cyan",$)} `:"",o=t?u("cyan",j):"";return`${r}${a}${n}
|
|
42
|
+
${o}
|
|
43
|
+
`}}}}).prompt();function re(e){return e==null?[]:Array.isArray(e)?e:[e]}function gt(e,t,r,s){var n,l=e[t],a=~s.string.indexOf(t)?r==null||r===!0?"":String(r):typeof r=="boolean"?r:~s.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 bt(e,t){e=e||[],t=t||{};var r,s,n,l,a,o={_:[]},i=0,c=0,d=0,h=e.length;const m=t.alias!==void 0,f=t.unknown!==void 0,p=t.default!==void 0;if(t.alias=t.alias||{},t.string=re(t.string),t.boolean=re(t.boolean),m)for(r in t.alias)for(s=t.alias[r]=re(t.alias[r]),i=0;i<s.length;i++)(t.alias[s[i]]=s.concat(r)).splice(i,1);for(i=t.boolean.length;i-- >0;)for(s=t.alias[t.boolean[i]]||[],c=s.length;c-- >0;)t.boolean.push(s[c]);for(i=t.string.length;i-- >0;)for(s=t.alias[t.string[i]]||[],c=s.length;c-- >0;)t.string.push(s[c]);if(p){for(r in t.default)if(l=typeof t.default[r],s=t.alias[r]=t.alias[r]||[],t[l]!==void 0)for(t[l].push(r),i=0;i<s.length;i++)t[l].push(s[i])}const g=f?Object.keys(t.alias):[];for(i=0;i<h;i++){if(n=e[i],n==="--"){o._=o._.concat(e.slice(++i));break}for(c=0;c<n.length&&n.charCodeAt(c)===45;c++);if(c===0)o._.push(n);else if(n.substring(c,c+3)==="no-"){if(l=n.substring(c+3),f&&!~g.indexOf(l))return t.unknown(n);o[l]=!1}else{for(d=c+1;d<n.length&&n.charCodeAt(d)!==61;d++);for(l=n.substring(c,d),a=n.substring(++d)||i+1===h||(""+e[i+1]).charCodeAt(0)===45||e[++i],s=c===2?[l]:l,d=0;d<s.length;d++){if(l=s[d],f&&!~g.indexOf(l))return t.unknown("-".repeat(c)+l);gt(o,l,d+1<s.length||a,t)}}}if(p)for(r in t.default)o[r]===void 0&&(o[r]=t.default[r]);if(m)for(r in o)for(s=t.alias[r]||[];s.length>0;)o[s.shift()]=o[r];return o}var z={exports:{}},Fe;function yt(){if(Fe)return z.exports;Fe=1;let e=process||{},t=e.argv||[],r=e.env||{},s=!(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,i,c=o)=>d=>{let h=""+d,m=h.indexOf(i,o.length);return~m?o+l(h,i,c,m)+i:o+h+i},l=(o,i,c,d)=>{let h="",m=0;do h+=o.substring(m,d)+c,m=d+i.length,d=o.indexOf(i,m);while(~d);return h+o.substring(m)},a=(o=s)=>{let i=o?n:()=>String;return{isColorSupported:o,reset:i("\x1B[0m","\x1B[0m"),bold:i("\x1B[1m","\x1B[22m","\x1B[22m\x1B[1m"),dim:i("\x1B[2m","\x1B[22m","\x1B[22m\x1B[2m"),italic:i("\x1B[3m","\x1B[23m"),underline:i("\x1B[4m","\x1B[24m"),inverse:i("\x1B[7m","\x1B[27m"),hidden:i("\x1B[8m","\x1B[28m"),strikethrough:i("\x1B[9m","\x1B[29m"),black:i("\x1B[30m","\x1B[39m"),red:i("\x1B[31m","\x1B[39m"),green:i("\x1B[32m","\x1B[39m"),yellow:i("\x1B[33m","\x1B[39m"),blue:i("\x1B[34m","\x1B[39m"),magenta:i("\x1B[35m","\x1B[39m"),cyan:i("\x1B[36m","\x1B[39m"),white:i("\x1B[37m","\x1B[39m"),gray:i("\x1B[90m","\x1B[39m"),bgBlack:i("\x1B[40m","\x1B[49m"),bgRed:i("\x1B[41m","\x1B[49m"),bgGreen:i("\x1B[42m","\x1B[49m"),bgYellow:i("\x1B[43m","\x1B[49m"),bgBlue:i("\x1B[44m","\x1B[49m"),bgMagenta:i("\x1B[45m","\x1B[49m"),bgCyan:i("\x1B[46m","\x1B[49m"),bgWhite:i("\x1B[47m","\x1B[49m"),blackBright:i("\x1B[90m","\x1B[39m"),redBright:i("\x1B[91m","\x1B[39m"),greenBright:i("\x1B[92m","\x1B[39m"),yellowBright:i("\x1B[93m","\x1B[39m"),blueBright:i("\x1B[94m","\x1B[39m"),magentaBright:i("\x1B[95m","\x1B[39m"),cyanBright:i("\x1B[96m","\x1B[39m"),whiteBright:i("\x1B[97m","\x1B[39m"),bgBlackBright:i("\x1B[100m","\x1B[49m"),bgRedBright:i("\x1B[101m","\x1B[49m"),bgGreenBright:i("\x1B[102m","\x1B[49m"),bgYellowBright:i("\x1B[103m","\x1B[49m"),bgBlueBright:i("\x1B[104m","\x1B[49m"),bgMagentaBright:i("\x1B[105m","\x1B[49m"),bgCyanBright:i("\x1B[106m","\x1B[49m"),bgWhiteBright:i("\x1B[107m","\x1B[49m")}};return z.exports=a(),z.exports.createColors=a,z.exports}var xt=yt();const $t=ze(xt),{cyan:wt,magenta:vt,yellow:Et}=$t,J=[{name:"engine",display:"Engine",description:"with the PlayCanvas Engine API directly",color:Et},{name:"react",display:"React",description:"with @playcanvas/react",color:wt},{name:"web-components",display:"Web Components",description:"with @playcanvas/web-components",color:vt}],W=[{name:"spinning-cube",display:"Spinning Cube",description:"a rotating cube and a light"},{name:"interactive-sphere",display:"Interactive Sphere",description:"orbit controls, a grid and pointer picking"},{name:"model-viewer",display:"Model Viewer",description:"a glTF model, orbit controls and a procedural sky"},{name:"splat-viewer",display:"Splat Viewer",description:"a Gaussian splat and orbit controls"}],_t=async({argBoilerplate:e,yes:t,cancel:r})=>{if(e&&W.some(n=>n.name===e))return e;if(!process.stdout.isTTY||t){if(!e)return W[0].name;throw new Error(`Unknown boilerplate: ${e}. Choose from: ${W.map(n=>n.name).join(", ")}`)}const s=await te({message:e?`"${e}" isn't a valid boilerplate. Please choose from below: `:"Select a boilerplate:",initialValue:W[0].name,options:W.map(n=>({label:n.display,value:n.name,hint:n.description}))});return D(s)&&r(),s},St=async({argTemplate:e,yes:t,cancel:r})=>{if(e&&J.some(n=>n.name===e))return e;if(e&&(!process.stdout.isTTY||t))throw new Error(`Unknown template: ${e}. Choose from: ${J.map(n=>n.name).join(", ")}`);if(t)return J[0].name;const s=await te({message:e?`"${e}" isn't a valid template. Please choose from below: `:"Select a template:",options:J.map(n=>({label:n.color(n.display),value:n.name,hint:n.description}))});return D(s)&&r(),s},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,"-"),It=async({targetDir:e,yes:t,cancel:r})=>{let s=y.basename(y.resolve(e));if(!Te(s)){if(t)return ie(s);const n=await Ie({message:"Package name:",defaultValue:ie(s),placeholder:ie(s),validate:l=>{if(!l||!Te(l))return"Invalid package.json name"}});if(D(n))return r();s=n}return s},se=e=>e.trim().replace(/\/+$/g,""),Ft=e=>{const t=_.readdirSync(e);return t.length===0||t.length===1&&t[0]===".git"},Tt=e=>{if(_.existsSync(e))for(const t of _.readdirSync(e))t!==".git"&&_.rmSync(y.resolve(e,t),{recursive:!0,force:!0})},Ct=async({argTargetDir:e,defaultTargetDir:t,yes:r,cancel:s})=>{let n=e?se(String(e)):void 0;if(!n&&r)return t;if(!n){const l=await Ie({message:"Project name:",defaultValue:t,placeholder:t});D(l)&&s(),n=se(l)}return n},Rt=async({targetDir:e,argOverwrite:t,yes:r,cancel:s})=>{if(_.existsSync(e)&&!Ft(e)){if(r&&!t)throw new Error(`Target directory "${e}" is not empty. Pass --overwrite to remove its contents.`);const n=t?"yes":await te({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(D(n))return s();switch(n){case"yes":Tt(e);break;case"no":s();return}}},kt=e=>{const{cwd:t,targetDir:r,template:s,boilerplate:n,packageName:l,skills:a,renameFiles:o}=e,i=y.resolve(t,r);_.mkdirSync(i,{recursive:!0});const c=process.env.npm_config_user_agent?.split("/")[0]??"npm";O.step(`Creating project in ${i}...`);const d=ke(import.meta.url),h=y.dirname(d),m=[y.resolve(h,"../templates"),y.resolve(h,"../../templates")].find(v=>_.existsSync(y.join(v,s)));if(!m)throw new Error(`Template directory not found for: ${s}`);const f=y.join(m,s),p=y.join(m,"_shared",n);_.existsSync(p)&&_.cpSync(p,i,{recursive:!0});for(const v of["base",n]){const I=y.join(f,v);if(!_.existsSync(I))throw new Error(`Boilerplate directory not found: ${s}/${v}`);_.cpSync(I,i,{recursive:!0})}for(const[v,I]of Object.entries(o)){const b=y.join(i,v);_.existsSync(b)&&_.renameSync(b,y.join(i,I))}const g=y.join(i,"package.json"),S=JSON.parse(_.readFileSync(g,"utf-8"));if(S.name=l,_.writeFileSync(g,JSON.stringify(S,null,4)+`
|
|
44
|
+
`),a){const v=[y.resolve(h,"../skills"),y.resolve(h,"../../skills")].find(b=>_.existsSync(b));if(!v)throw new Error("Skills directory not found");const I=_.readdirSync(v,{withFileTypes:!0}).filter(b=>b.isDirectory()).map(b=>b.name);for(const b of[".claude/skills",".agents/skills"])for(const E of I)_.cpSync(y.join(v,E),y.join(i,b,E),{recursive:!0});O.step("Added PlayCanvas agent skills (opt out with --no-skills)")}let w=`Project created. Now run:
|
|
45
|
+
`;const T=y.isAbsolute(r)?i:y.relative(t,i);switch(i!==t&&(w+=`
|
|
46
|
+
cd ${T.includes(" ")?`"${T}"`:T}`),c){case"yarn":w+=`
|
|
47
|
+
yarn`,w+=`
|
|
48
|
+
yarn dev`;break;default:w+=`
|
|
49
|
+
${c} install`,w+=`
|
|
50
|
+
${c} run dev`;break}pt(w)},M=bt(process.argv.slice(2),{alias:{h:"help",t:"template",b:"boilerplate",y:"yes"},boolean:["help","overwrite","yes"],string:["template","boilerplate"]}),At=process.cwd(),Bt=`Usage: npm create playcanvas@latest [OPTION]...
|
|
51
51
|
|
|
52
52
|
Create a new PlayCanvas project with TypeScript.
|
|
53
53
|
With no arguments, start the CLI in interactive mode.
|
|
54
54
|
|
|
55
55
|
Options:
|
|
56
56
|
-t, --template NAME use a specific template
|
|
57
|
+
-b, --boilerplate NAME use a specific boilerplate
|
|
57
58
|
--overwrite remove existing files from a non-empty target directory
|
|
59
|
+
--no-skills omit the PlayCanvas agent skills
|
|
60
|
+
-y, --yes skip the prompts and take the defaults
|
|
58
61
|
-h, --help show help
|
|
59
|
-
`,
|
|
62
|
+
`,Lt={_gitignore:".gitignore"},Ot="playcanvas-project",Mt=async()=>{const e=M._[0]?se(String(M._[0])):void 0,t=M.template,r=M.boilerplate,s=M.overwrite,n=M.yes;if(M.help){console.log(Bt);return}const l=()=>{dt("Operation cancelled"),process.exit(0)},a=await Ct({argTargetDir:e,defaultTargetDir:Ot,yes:n,cancel:l});await Rt({targetDir:a,argOverwrite:s,yes:n,cancel:l});const o=await It({targetDir:a,yes:n,cancel:l}),i=await St({argTemplate:t,yes:n,cancel:l}),c=await _t({argBoilerplate:r,yes:n,cancel:l}),d=M.skills??!0;kt({cwd:At,targetDir:a,template:i,boilerplate:c,packageName:o,skills:d,renameFiles:Lt})};Mt().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.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Scaffold a PlayCanvas project",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"playcanvas",
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"template",
|
|
10
10
|
"vite",
|
|
11
11
|
"react",
|
|
12
|
+
"web-components",
|
|
12
13
|
"typescript",
|
|
13
14
|
"webgl",
|
|
14
15
|
"webgpu",
|
|
@@ -27,6 +28,7 @@
|
|
|
27
28
|
"create-playcanvas": "dist/index.mjs"
|
|
28
29
|
},
|
|
29
30
|
"files": [
|
|
31
|
+
"skills",
|
|
30
32
|
"templates",
|
|
31
33
|
"dist"
|
|
32
34
|
],
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: add-effects
|
|
3
|
+
description: Use when adding transient visual effects or trails to a PlayCanvas app to select an Engine-native implementation, place and layer it correctly, manage its lifecycle, and verify the rendered result.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Effects and game feel
|
|
7
|
+
|
|
8
|
+
Add transient effects as short-lived, self-owning entities triggered by application events, not as
|
|
9
|
+
permanent scene fixtures.
|
|
10
|
+
|
|
11
|
+
Reuse before authoring. Adapt the closest official particle example with `find-examples`, prefer the
|
|
12
|
+
Engine's built-in particle component over a hand-written system, and discover any shipped trail or
|
|
13
|
+
effect script with `reuse-scripts`.
|
|
14
|
+
|
|
15
|
+
Copy particle curve construction from the installed example exactly. `CurveSet` takes one key array
|
|
16
|
+
per channel, such as `new CurveSet([0, x0, 1, x1], [0, y0, 1, y1], [0, z0, 1,
|
|
17
|
+
z1])`; do not flatten three channels into one array. A malformed curve can type-check and then fail
|
|
18
|
+
inside the particle texture upload. Create one emitter, render a frame, and require empty runtime
|
|
19
|
+
and console diagnostics before reusing the helper for smoke, spray, or splashes.
|
|
20
|
+
|
|
21
|
+
For a polished wake, smoke, or spray requirement, do not stop at scaled planes, crossed billboards,
|
|
22
|
+
or generic render primitives. Use the built-in particle component for smoke and spray; reserve a
|
|
23
|
+
custom mesh and material for a continuously sampled wake ribbon. Verify tint and blending against
|
|
24
|
+
the actual scene lighting and surface.
|
|
25
|
+
|
|
26
|
+
## Place and layer
|
|
27
|
+
|
|
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.
|
|
30
|
+
- Keep additive or transparent effects above the surface they sit on and biased toward the camera so
|
|
31
|
+
they do not z-fight. Inspect soft transparency at grazing angles for hard edges, black quads, or
|
|
32
|
+
dropout.
|
|
33
|
+
- Size and orient effects from the emitter's calibrated bounds, not a guessed constant.
|
|
34
|
+
|
|
35
|
+
## Moving trails
|
|
36
|
+
|
|
37
|
+
- Treat built-in render primitives as blocking geometry for soft effects. Follow the closest
|
|
38
|
+
official example to choose particles, a custom mesh, textures, materials, and blending for the
|
|
39
|
+
intended visual behavior.
|
|
40
|
+
- Match every custom mesh's vertex streams to its material. A lit `StandardMaterial` needs normals;
|
|
41
|
+
calculate and set them before `mesh.update()`, or deliberately use an unlit material. Treat a
|
|
42
|
+
missing vertex-attribute diagnostic as a rendering failure even when some frames still appear.
|
|
43
|
+
For a dynamic horizontal ribbon created with `setVertexStream`, allocate a normal array alongside
|
|
44
|
+
positions, fill every vertex with `(0, 1, 0)`, and upload it as `SEMANTIC_NORMAL` before the first
|
|
45
|
+
`update()`. Recheck this whenever changing the ribbon's material type.
|
|
46
|
+
- Never attach an empty dynamic mesh to an enabled render component. Upload a valid initial vertex
|
|
47
|
+
and index set before creating its `MeshInstance`, or keep the render entity disabled until the
|
|
48
|
+
first complete update. On reset, disable it or restore valid seed geometry; do not call
|
|
49
|
+
`mesh.clear()` while an enabled renderer still references it. Reflection and refraction cameras
|
|
50
|
+
render the same mesh and can expose this lifecycle bug before the gameplay camera does.
|
|
51
|
+
- Judge a trail from its intended cameras. If it reads as a flat decal, use the reference example to
|
|
52
|
+
add the minimum depth cue needed rather than stacking arbitrary geometry.
|
|
53
|
+
- Sample the trail continuously. Append segments on a small time- or distance-step and interpolate
|
|
54
|
+
between them; coarse per-distance chunks make the trail stutter and break into visible dashes as
|
|
55
|
+
the object speeds up.
|
|
56
|
+
- Anchor it to the mover's contact or exhaust point, scale emission rate and length with speed, and
|
|
57
|
+
fade the tail so it dissolves instead of cutting off with a hard edge.
|
|
58
|
+
|
|
59
|
+
## Camera shake and hit feedback
|
|
60
|
+
|
|
61
|
+
- Drive camera shake as a decaying offset on a camera parent or an additive layer; never write it
|
|
62
|
+
into the smoothed chase position, or it fights the follow. Keep shake and follow separate, as the
|
|
63
|
+
orbit-camera rule in `apply-conventions` requires.
|
|
64
|
+
- Stagger the parts of a compound effect over a few frames — flash, then smoke, then splash — so an
|
|
65
|
+
event reads as a sequence rather than a single pop.
|
|
66
|
+
- Keep the readable peak long enough for the gameplay camera to see the layers together. A flash can
|
|
67
|
+
be brief, but the longer-lived layers should overlap for several frames rather than leaving the
|
|
68
|
+
primary action frame visually identical to the idle state.
|
|
69
|
+
- For side-mounted emitters, derive every spawn point and direction from calibrated world-space
|
|
70
|
+
mounts on the mover. Confirm the effect starts outside the source geometry from both sides; an
|
|
71
|
+
active cooldown with only a tiny flash elsewhere in the frame is failed feedback.
|
|
72
|
+
|
|
73
|
+
## Prove it fires
|
|
74
|
+
|
|
75
|
+
Trigger each effect through real gameplay input and confirm with a screenshot captured at its peak,
|
|
76
|
+
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.
|
|
79
|
+
|
|
80
|
+
Choose the authoring surface with the `build-app` skill; create effects through its entity and
|
|
81
|
+
component primitives and own their lifecycle in the surface that spawned them.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: apply-conventions
|
|
3
|
+
description: Use when writing or reviewing PlayCanvas Engine code involving entity transforms, cameras, physics, materials, model orientation, bounds, or imports to apply the Engine's coordinate and API conventions.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Engine conventions
|
|
7
|
+
|
|
8
|
+
Read `package.json` and the installed `playcanvas` source before relying on version-sensitive API
|
|
9
|
+
details. Apply these stable conventions throughout the project.
|
|
10
|
+
|
|
11
|
+
## Coordinates and angles
|
|
12
|
+
|
|
13
|
+
- Use the right-handed coordinate system: +Y is up and the gameplay ground plane is XZ.
|
|
14
|
+
- Treat `entity.forward` and the camera viewing direction as -Z; `entity.right` is +X and
|
|
15
|
+
`entity.up` is +Y. `lookAt` points -Z at the target.
|
|
16
|
+
- Expect glTF models authored toward +Z to need a yaw correction. Inspect and calibrate each
|
|
17
|
+
directional model instead of applying one global correction.
|
|
18
|
+
- Pass degrees in XYZ order to high-level angle APIs.
|
|
19
|
+
- Keep pitch, yaw, and roll as application state. Do not repeatedly read `getEulerAngles()` and
|
|
20
|
+
feed its decomposition back into `setEulerAngles()`.
|
|
21
|
+
- Interpolate periodic angles with `math.lerpAngle()` or rotations with `Quat.slerp()`, not scalar
|
|
22
|
+
lerp across the 0/360 seam.
|
|
23
|
+
|
|
24
|
+
## Transforms and units
|
|
25
|
+
|
|
26
|
+
- Use `setPosition`, `setRotation`, and `setEulerAngles` for world transforms.
|
|
27
|
+
- Use the `setLocal*` variants below a parent.
|
|
28
|
+
- Treat units as metres. Physics gravity defaults to `(0, -9.81, 0)`.
|
|
29
|
+
- Keep gameplay transforms on a semantic root and asset-authoring corrections on a child.
|
|
30
|
+
- Do not scale rigid-body roots or skinned bones.
|
|
31
|
+
|
|
32
|
+
## Cameras, materials, and bounds
|
|
33
|
+
|
|
34
|
+
- Perspective camera FOV is vertical and defaults to 45 degrees.
|
|
35
|
+
- Smooth an orbit camera's focus, yaw, pitch, and distance, then derive its position. Do not
|
|
36
|
+
`Vec3.lerp()` between orbit positions on opposite sides of the focus; the chord crosses the subject.
|
|
37
|
+
- Before writing custom camera input, inspect the installed controller script and its matching
|
|
38
|
+
Engine example. Preserve their gesture mapping and damping unless the product explicitly asks
|
|
39
|
+
for different behavior; do not derive signs from memory.
|
|
40
|
+
- Define the input contract in screen space, then verify positive and negative movement on both axes
|
|
41
|
+
with real input after a rendered frame. Judge the camera basis or a projected fixed landmark, not
|
|
42
|
+
world-space camera coordinates or internal angle signs; both can approve inverted behavior.
|
|
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.
|
|
46
|
+
- Entities do not have an `aabb`. Union descendant `meshInstance.aabb` values after transforms have
|
|
47
|
+
synchronized.
|
|
48
|
+
- Let `Mesh.update()` calculate local bounds. If passing `false` for `updateBoundingBox`, assign an
|
|
49
|
+
explicit `mesh.aabb` before the mesh can be culled.
|
|
50
|
+
- Expand custom-mesh bounds by the maximum vertex-shader displacement; CPU bounds do not observe
|
|
51
|
+
shader deformation.
|
|
52
|
+
- Treat offline skinned-mesh bounds as bind-pose estimates and confirm animated poses at runtime.
|
|
53
|
+
|
|
54
|
+
## Imports
|
|
55
|
+
|
|
56
|
+
Use named ESM imports from `playcanvas`. Import production scripts only from
|
|
57
|
+
`playcanvas/scripts/esm/**`; legacy sibling script directories depend on a global Engine namespace.
|
|
58
|
+
|
|
59
|
+
## Runtime confirmation
|
|
60
|
+
|
|
61
|
+
Offline measurements cannot prove facing, animated poses, or final placement. Confirm each one in the
|
|
62
|
+
running application:
|
|
63
|
+
|
|
64
|
+
- Start the project's own dev server or build; do not construct a separate harness.
|
|
65
|
+
- Log the value from inside the Engine, using `getPosition()`, `getEulerAngles()`, a
|
|
66
|
+
`meshInstance.aabb`, or an anim component's active state, from a `Script` or an update callback.
|
|
67
|
+
- For large or vertex-displaced meshes, inspect their runtime AABB and `visibleThisFrame` after a
|
|
68
|
+
rendered frame from at least two representative camera angles.
|
|
69
|
+
- Confirm a directional model's visible front aligns with its semantic root's `forward`; a stored
|
|
70
|
+
yaw or runtime transform alone cannot prove mesh facing.
|
|
71
|
+
- Read that logged value from the browser console. Screenshots judge composition and orientation;
|
|
72
|
+
numeric claims require logged values.
|
|
73
|
+
- Record the confirmed value in application state or an asset tuning record, then remove the logging.
|