create-playcanvas 0.10.1 → 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 +26 -3
- package/dist/index.mjs +41 -38
- package/package.json +6 -3
- 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/_shared/web-lens/README.md +42 -0
- package/templates/_shared/web-lens/package.json +9 -0
- package/templates/_shared/web-lens/scripts/package-lens.mjs +231 -0
- package/templates/_shared/web-lens/src/dodge-game.ts +271 -0
- package/templates/_shared/web-lens/src/lens.css +155 -0
- package/templates/_shared/web-lens/src/lens.ts +91 -0
- package/templates/engine/base/README.md +1 -1
- package/templates/engine/base/package.json +4 -4
- 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/engine/web-lens/index.html +26 -0
- package/templates/engine/web-lens/src/main.ts +89 -0
- package/templates/engine/web-lens/vite.config.ts +44 -0
- package/templates/index.ts +6 -0
- package/templates/react/base/README.md +1 -1
- package/templates/react/base/package.json +6 -6
- 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/react/{splat-viewer → web-lens}/index.html +2 -3
- package/templates/react/web-lens/src/App.tsx +85 -0
- package/templates/react/web-lens/vite.config.ts +46 -0
- package/templates/web-components/base/README.md +1 -1
- package/templates/web-components/base/package.json +4 -4
- 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/web-components/web-lens/index.html +36 -0
- package/templates/web-components/web-lens/src/main.ts +47 -0
- package/templates/web-components/web-lens/vite.config.ts +44 -0
- 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/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,9 +38,19 @@ 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
|
-

|
|
44
|
+
|
|
45
|
+
## Snapchat Web Lenses
|
|
46
|
+
|
|
47
|
+
The `web-lens` starter is a portrait game ready to publish to Snapchat as a [Web Lens](https://developers.snap.com/web-lenses/home), with the [Web Lens API](https://developers.snap.com/web-lenses/web-lens-api) wired up for leaderboards, Bitmoji and storage:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
npm create playcanvas@latest my-lens -- --starter web-lens
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Its `npm run package` script builds the game, checks it against the Web Lens upload rules (size limits, relative paths and no network access) and writes the zip to upload through [My Lenses](https://my-lenses.snapchat.com/).
|
|
44
54
|
|
|
45
55
|
## CLI Options
|
|
46
56
|
|
|
@@ -59,7 +69,7 @@ npm create playcanvas@latest my-game -- -f react -y
|
|
|
59
69
|
| `--yes` | `-y` | Skip the prompts and take the defaults |
|
|
60
70
|
| `--help` | `-h` | Show command help |
|
|
61
71
|
|
|
62
|
-
`--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.
|
|
63
73
|
|
|
64
74
|
## Agent skills
|
|
65
75
|
|
|
@@ -69,12 +79,25 @@ Every project includes [`@playcanvas/skills`](https://github.com/playcanvas/skil
|
|
|
69
79
|
|
|
70
80
|
```bash
|
|
71
81
|
npm install
|
|
82
|
+
npm test
|
|
72
83
|
npm run build
|
|
73
84
|
node dist/index.mjs
|
|
74
85
|
```
|
|
75
86
|
|
|
76
87
|
Contributions are welcome. Please open an issue before proposing a new format, starter or another substantial change.
|
|
77
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
|
+
|
|
78
101
|
## License
|
|
79
102
|
|
|
80
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.
|
|
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": {
|
|
@@ -48,8 +49,10 @@
|
|
|
48
49
|
"eslint": "^9.39.5",
|
|
49
50
|
"mri": "^1.2.0",
|
|
50
51
|
"picocolors": "^1.1.1",
|
|
51
|
-
"prettier": "^3.9.
|
|
52
|
-
"unbuild": "^3.6.1"
|
|
52
|
+
"prettier": "^3.9.10",
|
|
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
|
```
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
## Snapchat Web Lens
|
|
2
|
+
|
|
3
|
+
A portrait, one-thumb dodge game set up to run inside Snapchat as a [Web Lens](https://developers.snap.com/web-lenses/home). It submits each run to the Lens leaderboard, keeps the player's best score in Snapchat storage and shows their Bitmoji, all through the [Web Lens API](https://developers.snap.com/web-lenses/web-lens-api).
|
|
4
|
+
|
|
5
|
+
### Controls
|
|
6
|
+
|
|
7
|
+
Drag left and right to steer the ball around the falling blocks. Tap to start, and tap again after a crash to play another run.
|
|
8
|
+
|
|
9
|
+
### Publishing to Snapchat
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm run package
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
This builds the game into `dist/lens/`, checks it against the [Web Lens upload rules](https://developers.snap.com/web-lenses/requirements) and writes a zip to `dist/` to upload through [My Lenses](https://my-lenses.snapchat.com/). It writes no zip if:
|
|
16
|
+
|
|
17
|
+
- the zip is over 8 MB, or `index.html` is over 1 MB
|
|
18
|
+
- the build refers to one of its own files by an absolute path, such as `/assets/game.js`
|
|
19
|
+
- the build loads anything from another host, such as a script from a CDN or a web font
|
|
20
|
+
- the build uses something the Lens sandbox blocks, such as an `<iframe>` or a web app manifest
|
|
21
|
+
|
|
22
|
+
It warns about any other host your scripts mention, since a Lens can't reach the network. To check the build itself, `npm run start` serves it under the same rules, so a request for anything outside the zip fails in the browser console as it would in Snapchat.
|
|
23
|
+
|
|
24
|
+
### How it fits together
|
|
25
|
+
|
|
26
|
+
- `src/dodge-game.ts` is the game, an engine script that fires a `game:state` app event for the HUD to follow.
|
|
27
|
+
- `src/lens.ts` wraps the Web Lens API. Outside Snapchat the API answers from a built-in stub with canned data, so the game runs in any browser, and every call falls back to playing on without the feature.
|
|
28
|
+
- `src/lens.css` fills the phone screen and shows a 9:16 column on wider screens. It keeps the HUD clear of the controls Snapchat draws over the top of a Lens, and `npm run dev` marks the room they take.
|
|
29
|
+
- `vite.config.ts` makes every URL in the build relative, which a Lens needs.
|
|
30
|
+
|
|
31
|
+
### Make it your own
|
|
32
|
+
|
|
33
|
+
The game is the only part that's specific to Dodge, so replace it and keep the rest:
|
|
34
|
+
|
|
35
|
+
- **Replace** `src/dodge-game.ts` with your game. The HUD follows its `game:state` app event and shows the text in its `CARDS`, so fire the same event from your game, or rework the HUD in `index.html` and `src/main.ts` (`src/App.tsx` in React). Rename Dodge in the page `<title>` too.
|
|
36
|
+
- **Keep** `src/lens.ts`, `src/lens.css`, `vite.config.ts` and `scripts/package-lens.mjs`. None of them depends on the game.
|
|
37
|
+
- **Report each run to Snapchat** by calling `recordScore(score)` from `src/lens.ts` when it ends. That submits the score to the leaderboard, saves a new best to Snapchat storage and resolves the best score. Call `showLeaderboard()` from a button, and `loadBest()` and `loadAvatar()` at startup for the saved best and the player's Bitmoji.
|
|
38
|
+
- **Stick to what a Lens needs:** a portrait layout, a HUD kept inside the safe area with the `--inset-*` properties from `src/lens.css`, a pause when the page is hidden, audio that starts from a tap, and every asset bundled into the build, since a Lens can't reach the network.
|
|
39
|
+
|
|
40
|
+
Lenses run on phones, in portrait. To try the game on yours, run `npm run dev -- --host` and open the network URL it prints.
|
|
41
|
+
|
|
42
|
+
When you're ready to publish, follow Snap's [publishing guide](https://developers.snap.com/web-lenses/publishing): you'll also need a 512 × 512 PNG icon and a 5-second, 720 × 1280 preview video.
|