create-playcanvas 0.16.1 → 0.17.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 +16 -16
- package/dist/index.mjs +20 -20
- package/package.json +2 -2
- package/templates/_shared/{ar-placement → ar}/README.md +1 -1
- package/templates/_shared/blank/README.md +7 -0
- package/templates/_shared/first-person-game/AGENTS.md +11 -0
- package/templates/_shared/first-person-game/README.md +21 -0
- package/templates/_shared/first-person-game/public/level.glb +0 -0
- package/templates/_shared/first-person-game/src/game.css +47 -0
- package/templates/_shared/first-person-game/src/game.ts +232 -0
- package/templates/_shared/{sprite-game → platformer}/README.md +1 -1
- package/templates/_shared/{sprite-game/src/sprite-game.ts → platformer/src/platformer.ts} +3 -3
- package/templates/_shared/{third-person-controller → third-person-game}/README.md +1 -1
- package/templates/_shared/{vr-starter → vr}/README.md +1 -1
- package/templates/engine/{ar-placement → ar}/index.html +2 -2
- package/templates/engine/base/package.json +2 -2
- package/templates/engine/first-person-game/index.html +26 -0
- package/templates/engine/first-person-game/src/main.ts +167 -0
- package/templates/engine/{sprite-game → platformer}/index.html +2 -2
- package/templates/engine/{sprite-game → platformer}/src/main.ts +2 -2
- package/templates/engine/{third-person-controller → third-person-game}/index.html +2 -2
- package/templates/engine/{vr-starter → vr}/index.html +2 -2
- package/templates/index.ts +14 -14
- package/templates/react/{ar-placement → ar}/src/App.tsx +1 -1
- package/templates/react/base/package.json +2 -2
- package/templates/react/first-person-game/src/App.tsx +140 -0
- package/templates/react/{sprite-game → platformer}/src/App.tsx +3 -3
- package/templates/react/{third-person-controller → third-person-game}/src/App.tsx +1 -1
- package/templates/react/{vr-starter → vr}/src/App.tsx +1 -1
- package/templates/web-components/{ar-placement → ar}/index.html +2 -2
- package/templates/web-components/base/package.json +2 -2
- package/templates/web-components/first-person-game/index.html +80 -0
- package/templates/web-components/first-person-game/src/main.ts +74 -0
- package/templates/web-components/{sprite-game → platformer}/index.html +2 -2
- package/templates/web-components/{sprite-game → platformer}/src/main.ts +2 -2
- package/templates/web-components/{third-person-controller → third-person-game}/index.html +2 -2
- package/templates/web-components/{vr-starter → vr}/index.html +2 -2
- package/templates/_shared/first-person-controller/README.md +0 -7
- package/templates/_shared/first-person-controller/src/controller.ts +0 -18
- package/templates/_shared/spinning-cube/README.md +0 -7
- package/templates/engine/first-person-controller/index.html +0 -19
- package/templates/engine/first-person-controller/src/main.ts +0 -143
- package/templates/react/first-person-controller/src/App.tsx +0 -115
- package/templates/web-components/first-person-controller/index.html +0 -163
- package/templates/web-components/first-person-controller/src/main.ts +0 -15
- /package/templates/_shared/{ar-placement → ar}/src/ar-placement.ts +0 -0
- /package/templates/_shared/{first-person-controller → first-person-game}/src/scripts.d.ts +0 -0
- /package/templates/_shared/{third-person-controller → third-person-game}/src/controller.ts +0 -0
- /package/templates/_shared/{third-person-controller → third-person-game}/src/scripts.d.ts +0 -0
- /package/templates/_shared/{vr-starter → vr}/src/vr.ts +0 -0
- /package/templates/_shared/{vr-starter → vr}/src/xr.d.ts +0 -0
- /package/templates/engine/{ar-placement → ar}/src/main.ts +0 -0
- /package/templates/engine/{spinning-cube → blank}/src/main.ts +0 -0
- /package/templates/engine/{third-person-controller → third-person-game}/src/main.ts +0 -0
- /package/templates/engine/{vr-starter → vr}/src/main.ts +0 -0
- /package/templates/react/{spinning-cube → blank}/src/App.tsx +0 -0
- /package/templates/web-components/{ar-placement → ar}/src/main.ts +0 -0
- /package/templates/web-components/{spinning-cube → blank}/index.html +0 -0
- /package/templates/web-components/{spinning-cube → blank}/src/main.ts +0 -0
- /package/templates/web-components/{third-person-controller → third-person-game}/src/main.ts +0 -0
- /package/templates/web-components/{vr-starter → vr}/src/main.ts +0 -0
package/README.md
CHANGED
|
@@ -38,21 +38,21 @@ Every format uses TypeScript and includes Vite with hot reload, ESLint and Prett
|
|
|
38
38
|
|
|
39
39
|
## Starters
|
|
40
40
|
|
|
41
|
-
Every starter is a runnable scene, available in every format. Run the creator to browse them, or pass `--help` to list them; `
|
|
42
|
-
|
|
43
|
-
| Starter
|
|
44
|
-
|
|
|
45
|
-
| `
|
|
46
|
-
| `model-viewer`
|
|
47
|
-
| `splat-viewer`
|
|
48
|
-
| `product-configurator`
|
|
49
|
-
| `first-person-
|
|
50
|
-
| `third-person-
|
|
51
|
-
| `
|
|
52
|
-
| `web-lens`
|
|
53
|
-
| `scene-editor`
|
|
54
|
-
| `vr
|
|
55
|
-
| `ar
|
|
41
|
+
Every starter is a runnable scene, available in every format. Run the creator to browse them, or pass `--help` to list them; `blank` is the default.
|
|
42
|
+
|
|
43
|
+
| Starter | Category | Description |
|
|
44
|
+
| ---------------------- | -------- | --------------------------------------------------------------- |
|
|
45
|
+
| `blank` | Basics | Just the skeleton, with a rotating cube and a light |
|
|
46
|
+
| `model-viewer` | Viewers | A glTF model, orbit controls and a procedural sky |
|
|
47
|
+
| `splat-viewer` | Viewers | A Gaussian splat and orbit controls |
|
|
48
|
+
| `product-configurator` | Viewers | Product variants, materials and orbit controls |
|
|
49
|
+
| `first-person-game` | Games | A timed orb hunt across ramps, stairs and jumps |
|
|
50
|
+
| `third-person-game` | Games | An animated character, follow camera and locomotion |
|
|
51
|
+
| `platformer` | Games | An orthographic camera, sprite animation and input |
|
|
52
|
+
| `web-lens` | Games | A portrait game with Snapchat leaderboards, packaged for upload |
|
|
53
|
+
| `scene-editor` | Tools | Click to select, gizmos and a scene saved in the browser |
|
|
54
|
+
| `vr` | XR | Immersive VR, controllers and locomotion |
|
|
55
|
+
| `ar` | XR | Mobile AR, hit testing and object placement |
|
|
56
56
|
|
|
57
57
|

|
|
58
58
|
|
|
@@ -92,7 +92,7 @@ Use `.` as the project name to create the project in the current directory.
|
|
|
92
92
|
| `--yes` | `-y` | Skip the prompts and take the defaults |
|
|
93
93
|
| `--help` | `-h` | Show command help, with the formats and starters |
|
|
94
94
|
|
|
95
|
-
`--yes` takes `playcanvas-project`, the `engine` format and the `
|
|
95
|
+
`--yes` takes `playcanvas-project`, the `engine` format and the `blank` starter for anything you don't pass. It never touches files that are already there, so a non-empty target directory still needs `--keep` or `--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` (`-t`) and `--boilerplate` (`-b`) flags remain accepted for compatibility.
|
|
96
96
|
|
|
97
97
|
`--keep` adds the project to a directory that already holds files, such as a brief, assets or a README, and replaces none of them: where the project has a file of the same name, yours stays and the creator lists it. It refuses a directory that already has a `package.json`, since that is a project already. To add a project to the current directory:
|
|
98
98
|
|
package/dist/index.mjs
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{styleText as u}from"node:util";import
|
|
2
|
+
import{styleText as u}from"node:util";import P,{stdin as Ue,stdout as ye}from"node:process";import Ge from"node:readline";import"node:tty";import y from"node:fs";import b from"node:path";import Ke from"node:os";import{fileURLToPath as He}from"node:url";const ze=(()=>{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}})(),Je=e=>e===12288||e>=65281&&e<=65376||e>=65504&&e<=65510,Ye=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,qe=/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]|\u001b\]8;[^;]*;.*?(?:\u0007|\u001b\u005c)/y,Xe=/[\x00-\x08\x0A-\x1F\x7F-\x9F]{1,1000}/y,$e=/(?:(?![\uFF61-\uFF9F\uFF00-\uFFEF])[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}\p{Script=Tangut}]){1,1000}/yu,Qe=/\t{1,1000}/y,we=new RegExp("[\\u{1F1E6}-\\u{1F1FF}]{2}|\\u{1F3F4}[\\u{E0061}-\\u{E007A}]{2}[\\u{E0030}-\\u{E0039}\\u{E0061}-\\u{E007A}]{1,3}\\u{E007F}|(?:\\p{Emoji}\\uFE0F\\u20E3?|\\p{Emoji_Modifier_Base}\\p{Emoji_Modifier}?|\\p{Emoji_Presentation})(?:\\u200D(?:\\p{Emoji_Modifier_Base}\\p{Emoji_Modifier}?|\\p{Emoji_Presentation}|\\p{Emoji}\\uFE0F\\u20E3?))*","yu"),Ze=/(?:[\x20-\x7E\xA0-\xFF](?!\uFE0F)){1,1000}/y,et=new RegExp("\\p{M}+","gu"),tt={limit:1/0,ellipsis:""},xe=(e,t={},r={})=>{const i=t.limit??1/0,n=t.ellipsis??"",o=t?.ellipsisWidth??(n?xe(n,tt,r).width:0),l=0,a=r.controlWidth??0,s=r.tabWidth??8,c=r.emojiWidth??2,d=2,h=r.regularWidth??1,m=r.wideWidth??d,f=[[Ze,h],[qe,l],[Xe,a],[Qe,s],[we,c],[$e,m]];let g=0,w=0,_=e.length,F=0,j=!1,M=_,O=Math.max(0,i-o),v=0,k=0,$=0,I=0;e:for(;;){if(k>v||w>=_&&w>g){const R=e.slice(v,k)||e.slice(g,w);F=0;for(const D of R.replaceAll(et,"")){const A=D.codePointAt(0)||0;if(Je(A)?I=d:Ye(A)?I=m:I=h,$+I>O&&(M=Math.min(M,Math.max(v,g)+F)),$+I>i){j=!0;break e}F+=D.length,$+=I}v=k=0}if(w>=_)break e;for(let R=0,D=f.length;R<D;R++){const[A,H]=f[R];if(A.lastIndex=w,A.test(e)){if(F=A===$e?ze(e.slice(w,A.lastIndex)):A===we?1:A.lastIndex-w,I=F*H,$+I>O&&(M=Math.min(M,w+Math.floor((O-$)/H))),$+I>i){j=!0;break e}$+=I,v=g,k=w,w=g=A.lastIndex;continue e}}w+=1}return{width:j?O:$,index:j?M:_,truncated:j,ellipsed:j&&i>=o}},rt={limit:1/0,ellipsis:"",ellipsisWidth:0},W=(e,t={})=>xe(e,rt,t).width,Z="\x1B",ve="\x9B",it=39,ce="\x07",Se="[",st="]",Ee="m",ue=`${st}8;;`,_e=new RegExp(`(?:\\${Se}(?<code>\\d+)m|\\${ue}(?<uri>.*)${ce})`,"y"),Ie=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=>`${Z}${Se}${e}${Ee}`,ke=e=>`${Z}${ue}${e}${ce}`,he=(e,t,r)=>{const i=t[Symbol.iterator]();let n=!1,o=!1,l=e.at(-1),a=l===void 0?0:W(l),s=i.next(),c=i.next(),d=0;for(;!s.done;){const h=s.value,m=W(h);a+m<=r?e[e.length-1]+=h:(e.push(h),a=0),(h===Z||h===ve)&&(n=!0,o=t.startsWith(ue,d+1)),n?o?h===ce&&(n=!1,o=!1):h===Ee&&(n=!1):(a+=m,a===r&&!c.done&&(e.push(""),a=0)),s=c,c=i.next(),d+=h.length}l=e.at(-1),!a&&l!==void 0&&l.length&&e.length>1&&(e[e.length-2]+=e.pop())},nt=e=>{const t=e.split(" ");let r=t.length;for(;r&&!W(t[r-1]);)r--;return r===t.length?e:t.slice(0,r).join(" ")+t.slice(r).join("")},at=(e,t,r={})=>{if(r.trim!==!1&&e.trim()==="")return"";let i="",n,o;const l=e.split(" ");let a=[""],s=0;for(let h=0;h<l.length;h++){const m=l[h];if(r.trim!==!1){const g=a.at(-1)??"",w=g.trimStart();g.length!==w.length&&(a[a.length-1]=w,s=W(w))}h!==0&&(s>=t&&(r.wordWrap===!1||r.trim===!1)&&(a.push(""),s=0),(s||r.trim===!1)&&(a[a.length-1]+=" ",s++));const f=W(m);if(r.hard&&f>t){const g=t-s,w=1+Math.floor((f-g-1)/t);Math.floor((f-1)/t)<w&&a.push(""),he(a,m,t),s=W(a.at(-1)??"");continue}if(s+f>t&&s&&f){if(r.wordWrap===!1&&s<t){he(a,m,t),s=W(a.at(-1)??"");continue}a.push(""),s=0}if(s+f>t&&r.wordWrap===!1){he(a,m,t),s=W(a.at(-1)??"");continue}a[a.length-1]+=m,s+=f}r.trim!==!1&&(a=a.map(h=>nt(h)));const c=a.join(`
|
|
3
3
|
`);let d=!1;for(let h=0;h<c.length;h++){const m=c[h];if(i+=m,d)d=!1;else if(d=m>="\uD800"&&m<="\uDBFF",d)continue;if(m===Z||m===ve){_e.lastIndex=h+1;const g=_e.exec(c)?.groups;if(g?.code!==void 0){const w=Number.parseFloat(g.code);n=w===it?void 0:w}else g?.uri!==void 0&&(o=g.uri.length===0?void 0:g.uri)}if(c[h+1]===`
|
|
4
|
-
`){o&&(i+=
|
|
5
|
-
`&&(n&&Ie(n)&&(i+=Fe(n)),o&&(i+=
|
|
6
|
-
`)}const me="\x1B",S=`${me}[`,V={to(e,t){return t?`${S}${t+1};${e+1}H`:`${S}${e+1}G`},move(e,t){let r="";return e<0?r+=`${S}${-e}D`:e>0&&(r+=`${S}${e}C`),t<0?r+=`${S}${-t}A`:t>0&&(r+=`${S}${t}B`),r},up:(e=1)=>`${S}${e}A`,down:(e=1)=>`${S}${e}B`,forward:(e=1)=>`${S}${e}C`,backward:(e=1)=>`${S}${e}D`,nextLine:(e=1)=>`${S}E`.repeat(e),prevLine:(e=1)=>`${S}F`.repeat(e),left:`${S}G`,hide:`${S}?25l`,show:`${S}?25h`,save:`${me}7`,restore:`${me}8`},de={screen:`${S}2J`,up:(e=1)=>`${S}1J`.repeat(e),down:(e=1)=>`${S}J`.repeat(e),line:`${S}2K`,lineEnd:`${S}K`,lineStart:`${S}1K`,lines(e){let t="";for(let r=0;r<e;r++)t+=this.line+(r<e-1?V.up():"");return e&&(t+=V.left),t}};function te(e,t,r){if(!r.some(l=>!l.disabled))return e;const i=e+t,n=Math.max(r.length-1,0),o=i<0?n:i>n?0:i;return r[o]?.disabled?te(o,t<0?-1:1,r):o}const lt=["up","down","left","right","space","enter","cancel"],U={actions:new Set(lt),aliases:new Map([["k","up"],["j","down"],["h","left"],["l","right"],["","cancel"],["escape","cancel"]]),withGuide:!0};function ct(e){if(e!==void 0)return e;const t=process.env.ACCESSIBLE;return t!==void 0&&t!==""&&t!=="0"&&t!=="false"}function
|
|
4
|
+
`){o&&(i+=ke(""));const f=n?Ie(n):void 0;n&&f&&(i+=Fe(f))}else m===`
|
|
5
|
+
`&&(n&&Ie(n)&&(i+=Fe(n)),o&&(i+=ke(o)))}return i},ot=/\r?\n/;function ee(e,t,r){return String(e).normalize().split(ot).map(i=>at(i,t,r)).join(`
|
|
6
|
+
`)}const me="\x1B",S=`${me}[`,V={to(e,t){return t?`${S}${t+1};${e+1}H`:`${S}${e+1}G`},move(e,t){let r="";return e<0?r+=`${S}${-e}D`:e>0&&(r+=`${S}${e}C`),t<0?r+=`${S}${-t}A`:t>0&&(r+=`${S}${t}B`),r},up:(e=1)=>`${S}${e}A`,down:(e=1)=>`${S}${e}B`,forward:(e=1)=>`${S}${e}C`,backward:(e=1)=>`${S}${e}D`,nextLine:(e=1)=>`${S}E`.repeat(e),prevLine:(e=1)=>`${S}F`.repeat(e),left:`${S}G`,hide:`${S}?25l`,show:`${S}?25h`,save:`${me}7`,restore:`${me}8`},de={screen:`${S}2J`,up:(e=1)=>`${S}1J`.repeat(e),down:(e=1)=>`${S}J`.repeat(e),line:`${S}2K`,lineEnd:`${S}K`,lineStart:`${S}1K`,lines(e){let t="";for(let r=0;r<e;r++)t+=this.line+(r<e-1?V.up():"");return e&&(t+=V.left),t}};function te(e,t,r){if(!r.some(l=>!l.disabled))return e;const i=e+t,n=Math.max(r.length-1,0),o=i<0?n:i>n?0:i;return r[o]?.disabled?te(o,t<0?-1:1,r):o}const lt=["up","down","left","right","space","enter","cancel"],U={actions:new Set(lt),aliases:new Map([["k","up"],["j","down"],["h","left"],["l","right"],["","cancel"],["escape","cancel"]]),withGuide:!0};function ct(e){if(e!==void 0)return e;const t=process.env.ACCESSIBLE;return t!==void 0&&t!==""&&t!=="0"&&t!=="false"}function Ce(e,t){if(typeof e=="string")return U.aliases.get(e)===t;for(const r of e)if(r!==void 0&&Ce(r,t))return!0;return!1}function ut(e,t){if(e===t)return;const r=e.split(`
|
|
7
7
|
`),i=t.split(`
|
|
8
8
|
`),n=Math.max(r.length,i.length),o=[];for(let l=0;l<n;l++)r[l]!==i[l]&&o.push(l);return{lines:o,numLinesBefore:r.length,numLinesAfter:i.length,numLines:n}}globalThis.process.platform.startsWith("win");const pe=Symbol("clack:cancel");function Y(e){return e===pe}function re(e,t){const r=e;r.isTTY&&r.setRawMode(t)}const ie=e=>"columns"in e&&typeof e.columns=="number"?e.columns:80,Te=e=>"rows"in e&&typeof e.rows=="number"?e.rows:20;function se(e,t,r,i=r,n=r,o){const l=ie(e??ye),a=W(r);return ee(t,l-a,{hard:!0,trim:!1}).split(`
|
|
9
9
|
`).map((s,c,d)=>{const h=s;return c===0?`${i}${h}`:c===d.length-1?`${n}${h}`:`${r}${h}`}).join(`
|
|
10
|
-
`)}function ht(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)}let Re=class{input;output;_abortSignal;rl;opts;_render;_track=!1;_prevFrame="";_subscribers=new Map;_cursor=0;state="initial";error="";value;userInput="";get accessible(){return ct(this.opts.accessible)}constructor(t,r=!0){const{input:i=Ue,output:n=ye,render:o,signal:l,...a}=t;this.opts=a,this.onKeypress=this.onKeypress.bind(this),this.close=this.close.bind(this),this.render=this.render.bind(this),this._render=o.bind(this),this._track=r,this._abortSignal=l,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 o of i)o.cb(...r),o.once&&n.push(()=>i.splice(i.indexOf(o),1));for(const o of n)o()}prompt(){return new Promise(t=>{if(this._abortSignal){if(this._abortSignal.aborted)return this.state="cancel",this.close(),t(pe);this._abortSignal.addEventListener("abort",()=>{this.state="cancel",this.close()},{once:!0})}this.rl=Ge.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),re(this.input,!0),this.output.on("resize",this.render),this.render(),this.once("submit",()=>{this.output.write(V.show),this.output.off("resize",this.render),re(this.input,!1),t(this.value)}),this.once("cancel",()=>{this.output.write(V.show),this.output.off("resize",this.render),re(this.input,!1),t(pe)})})}_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&&U.aliases.has(r.name)&&this.emit("cursor",U.aliases.get(r.name)),U.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=ht(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")}
|
|
10
|
+
`)}function ht(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)}let Re=class{input;output;_abortSignal;rl;opts;_render;_track=!1;_prevFrame="";_subscribers=new Map;_cursor=0;state="initial";error="";value;userInput="";get accessible(){return ct(this.opts.accessible)}constructor(t,r=!0){const{input:i=Ue,output:n=ye,render:o,signal:l,...a}=t;this.opts=a,this.onKeypress=this.onKeypress.bind(this),this.close=this.close.bind(this),this.render=this.render.bind(this),this._render=o.bind(this),this._track=r,this._abortSignal=l,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 o of i)o.cb(...r),o.once&&n.push(()=>i.splice(i.indexOf(o),1));for(const o of n)o()}prompt(){return new Promise(t=>{if(this._abortSignal){if(this._abortSignal.aborted)return this.state="cancel",this.close(),t(pe);this._abortSignal.addEventListener("abort",()=>{this.state="cancel",this.close()},{once:!0})}this.rl=Ge.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),re(this.input,!0),this.output.on("resize",this.render),this.render(),this.once("submit",()=>{this.output.write(V.show),this.output.off("resize",this.render),re(this.input,!1),t(this.value)}),this.once("cancel",()=>{this.output.write(V.show),this.output.off("resize",this.render),re(this.input,!1),t(pe)})})}_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&&U.aliases.has(r.name)&&this.emit("cursor",U.aliases.get(r.name)),U.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=ht(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")}Ce([t,r?.name,r?.sequence],"cancel")&&(this.state="cancel"),(this.state==="submit"||this.state==="cancel")&&this.emit("finalize"),this.render(),(this.state==="submit"||this.state==="cancel")&&this.close()}}close(){this.input.unpipe(),this.input.removeListener("keypress",this.onKeypress),this.output.write(`
|
|
11
11
|
`),re(this.input,!1),this.rl?.close(),this.rl=void 0,this.emit(`${this.state}`,this.value),this.unsubscribe()}restoreCursor(){const t=ee(this._prevFrame,ie(this.output),{hard:!0,trim:!1}).split(`
|
|
12
12
|
`).length-1;this.output.write(V.move(-999,t*-1))}render(){const t=ee(this._render(this)??"",ie(this.output),{hard:!0,trim:!1});if(t!==this._prevFrame){if(this.state==="initial")this.output.write(V.hide);else{const r=ut(this._prevFrame,t),i=Te(this.output);if(this.restoreCursor(),r){const n=Math.max(0,r.numLinesAfter-i),o=Math.max(0,r.numLinesBefore-i);let l=r.lines.find(a=>a>=n);if(l===void 0){this._prevFrame=t;return}if(r.lines.length===1){this.output.write(V.move(0,l-o)),this.output.write(de.lines(1));const a=t.split(`
|
|
13
13
|
`);this.output.write(a[l]),this._prevFrame=t,this.output.write(V.move(0,a.length-l-1));return}else if(r.lines.length>1){if(n<o)l=n;else{const s=l-o;s>0&&this.output.write(V.move(0,s))}this.output.write(de.down());const a=t.split(`
|
|
14
14
|
`).slice(l);this.output.write(a.join(`
|
|
15
|
-
`)),this._prevFrame=t;return}}this.output.write(de.down())}this.output.write(t),this.state==="initial"&&(this.state="active"),this._prevFrame=t}}},mt=class extends Re{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?te(i,1,this.options):i,this.changeValue(),this.on("cursor",n=>{switch(n){case"left":case"up":this.cursor=te(this.cursor,-1,this.options);break;case"down":case"right":this.cursor=te(this.cursor,1,this.options);break}this.changeValue()})}};class dt extends Re{get userInputWithCursor(){if(this.state==="submit")return this.userInput;const t=this.userInput;if(this.cursor>=t.length)return`${this.userInput}\u2588`;const r=t.slice(0,this.cursor),i=t.slice(this.cursor,this.cursor+1),n=t.slice(this.cursor+1);return`${r}${u("inverse",i)}${n}`}get cursor(){return this._cursor}constructor(t){super({...t,initialUserInput:t.initialUserInput??t.initialValue}),this.on("userInput",r=>{this._setValue(r)}),this.on("finalize",()=>{this.value||(this.value=t.defaultValue),this.value===void 0&&(this.value="")})}}function pt(){return
|
|
16
|
-
`);return r&&n.push(u("cyan",G)),n}const
|
|
17
|
-
`);_.push(I),F+=I.length}if(F>d){let v=0,
|
|
15
|
+
`)),this._prevFrame=t;return}}this.output.write(de.down())}this.output.write(t),this.state==="initial"&&(this.state="active"),this._prevFrame=t}}},mt=class extends Re{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?te(i,1,this.options):i,this.changeValue(),this.on("cursor",n=>{switch(n){case"left":case"up":this.cursor=te(this.cursor,-1,this.options);break;case"down":case"right":this.cursor=te(this.cursor,1,this.options);break}this.changeValue()})}};class dt extends Re{get userInputWithCursor(){if(this.state==="submit")return this.userInput;const t=this.userInput;if(this.cursor>=t.length)return`${this.userInput}\u2588`;const r=t.slice(0,this.cursor),i=t.slice(this.cursor,this.cursor+1),n=t.slice(this.cursor+1);return`${r}${u("inverse",i)}${n}`}get cursor(){return this._cursor}constructor(t){super({...t,initialUserInput:t.initialUserInput??t.initialValue}),this.on("userInput",r=>{this._setValue(r)}),this.on("finalize",()=>{this.value||(this.value=t.defaultValue),this.value===void 0&&(this.value="")})}}function pt(){return P.platform!=="win32"?P.env.TERM!=="linux":!!P.env.CI||!!P.env.WT_SESSION||!!P.env.TERMINUS_SUBLIME||P.env.ConEmuTask==="{cmd::Cmder}"||P.env.TERM_PROGRAM==="Terminus-Sublime"||P.env.TERM_PROGRAM==="vscode"||P.env.TERM==="xterm-256color"||P.env.TERM==="alacritty"||P.env.TERMINAL_EMULATOR==="JetBrains-JediTerm"}const ft=pt(),B=(e,t)=>ft?e:t,je=B("\u25C6","*"),gt=B("\u25A0","x"),bt=B("\u25B2","x"),Ae=B("\u25C7","o"),E=B("\u2502","|"),G=B("\u2514","\u2014"),yt=B("\u25CF",">"),Oe=B("\u25CB"," "),$t=B("\u25CF","\u2022"),wt=B("\u25C6","*"),xt=B("\u25B2","!"),vt=B("\u25A0","x"),Be=e=>{switch(e){case"initial":case"active":return u("cyan",je);case"cancel":return u("red",gt);case"error":return u("yellow",bt);case"submit":return u("green",Ae);case"validating":return u("dim",je)}},St=e=>{switch(e){case"initial":case"active":return u("cyan",E);case"cancel":return u("red",E);case"error":return u("yellow",E);case"submit":return u("green",E)}};function Et(e,t,r){const i=r?`${u("cyan",E)} `:"",n=se(e,t.join(" \u2022 "),i).split(`
|
|
16
|
+
`);return r&&n.push(u("cyan",G)),n}const Le=(e,t,r,i,n,o=!1)=>{let l=t,a=0;if(o)for(let s=i-1;s>=r;s--){const c=e[s];if(c&&(l-=c.length),a++,l<=n)break}else for(let s=r;s<i;s++){const c=e[s];if(c&&(l-=c.length),a++,l<=n)break}return{lineCount:l,removals:a}},_t=({cursor:e,options:t,style:r,output:i=process.stdout,maxItems:n=Number.POSITIVE_INFINITY,columnPadding:o=0,rowPadding:l=4})=>{const a=ie(i)-o,s=Te(i),c=u("dim","..."),d=Math.max(s-l,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,g=h<t.length&&m+h<t.length;const w=Math.min(m+h,t.length),_=[];let F=0;f&&F++,g&&F++;const j=m+(f?1:0),M=w-(g?1:0);for(let v=j;v<M;v++){const k=t[v],$=k!==void 0?r(k,v===e):"",I=ee($,a,{hard:!0,trim:!1}).split(`
|
|
17
|
+
`);_.push(I),F+=I.length}if(F>d){let v=0,k=0,$=F;const I=e-j;let R=d;const D=()=>Le(_,$,0,I,R),A=()=>Le(_,$,I+1,_.length,R,!0);f?({lineCount:$,removals:v}=D(),$>R&&(g||(R-=1),{lineCount:$,removals:k}=A())):(g||(R-=1),{lineCount:$,removals:k}=A(),$>R&&(R-=1,{lineCount:$,removals:v}=D())),v>0&&(f=!0,_.splice(0,v)),k>0&&(g=!0,_.splice(_.length-k,k))}const O=[];f&&O.push(c);for(const v of _)for(const k of v)O.push(k);return g&&O.push(c),O};`${u("dim","\u2191/\u2193")}`,`${u("dim","Space:")}`,`${u("dim","Enter:")}`;const N={message:(e=[],{symbol:t=u("gray",E),secondarySymbol:r=u("gray",E),output:i=process.stdout,spacing:n=1,withGuide:o}={})=>{const l=[],a=o??U.withGuide,s=a?r:"",c=a?`${t} `:"",d=a?`${r} `:"";for(let m=0;m<n;m++)l.push(s);const h=Array.isArray(e)?e:e.split(`
|
|
18
18
|
`);if(h.length>0){const[m,...f]=h;m.length>0?l.push(`${c}${m}`):l.push(a?t:"");for(const g of f)g.length>0?l.push(`${d}${g}`):l.push(a?r:"")}i.write(`${l.join(`
|
|
19
19
|
`)}
|
|
20
20
|
`)},info:(e,t)=>{N.message(e,{...t,symbol:u("blue",$t)})},success:(e,t)=>{N.message(e,{...t,symbol:u("green",wt)})},step:(e,t)=>{N.message(e,{...t,symbol:u("green",Ae)})},warn:(e,t)=>{N.message(e,{...t,symbol:u("yellow",xt)})},warning:(e,t)=>{N.warn(e,t)},error:(e,t)=>{N.message(e,{...t,symbol:u("red",vt)})}},It=(e="",t)=>{const r=process.stdout,i=`${u("gray",G)} `;r.write(`${i}${u("red",e)}
|
|
@@ -22,19 +22,19 @@ import{styleText as u}from"node:util";import B,{stdin as Ue,stdout as ye}from"no
|
|
|
22
22
|
`)},Ft=(e="",t)=>{const r=process.stdout,i=`${u("gray",E)}
|
|
23
23
|
${u("gray",G)} `;r.write(`${i}${e}
|
|
24
24
|
|
|
25
|
-
`)},
|
|
25
|
+
`)},kt=[`${u("dim","\u2191/\u2193")} to navigate`,`${u("dim","Enter:")} confirm`],ne=(e,t)=>e.includes(`
|
|
26
26
|
`)?e.split(`
|
|
27
27
|
`).map(r=>t(r)).join(`
|
|
28
|
-
`):t(e),fe=e=>{const t=(i,n)=>{if(i===void 0)return"";const o=i.label??String(i.value);switch(n){case"disabled":return`${u("gray",Oe)} ${ne(o,l=>u("gray",l))}${i.hint?` ${u("dim",`(${i.hint??"disabled"})`)}`:""}`;case"selected":return`${ne(o,l=>u("dim",l))}`;case"active":return`${u("green",yt)} ${o}${i.hint?` ${u("dim",`(${i.hint})`)}`:""}`;case"cancelled":return`${ne(o,l=>u(["strikethrough","dim"],l))}`;default:return`${u("dim",Oe)} ${ne(o,l=>u("dim",l))}`}},r=e.showInstructions??!0;return new mt({options:e.options,signal:e.signal,input:e.input,output:e.output,initialValue:e.initialValue,render(){const i=e.withGuide??U.withGuide,n=`${
|
|
28
|
+
`):t(e),fe=e=>{const t=(i,n)=>{if(i===void 0)return"";const o=i.label??String(i.value);switch(n){case"disabled":return`${u("gray",Oe)} ${ne(o,l=>u("gray",l))}${i.hint?` ${u("dim",`(${i.hint??"disabled"})`)}`:""}`;case"selected":return`${ne(o,l=>u("dim",l))}`;case"active":return`${u("green",yt)} ${o}${i.hint?` ${u("dim",`(${i.hint})`)}`:""}`;case"cancelled":return`${ne(o,l=>u(["strikethrough","dim"],l))}`;default:return`${u("dim",Oe)} ${ne(o,l=>u("dim",l))}`}},r=e.showInstructions??!0;return new mt({options:e.options,signal:e.signal,input:e.input,output:e.output,initialValue:e.initialValue,render(){const i=e.withGuide??U.withGuide,n=`${Be(this.state)} `,o=`${St(this.state)} `,l=se(e.output,e.message,o,n),a=`${i?`${u("gray",E)}
|
|
29
29
|
`:""}${l}
|
|
30
30
|
`;switch(this.state){case"submit":{const s=i?`${u("gray",E)} `:"",c=se(e.output,t(this.options[this.cursor],"selected"),s);return`${a}${c}`}case"cancel":{const s=i?`${u("gray",E)} `:"",c=se(e.output,t(this.options[this.cursor],"cancelled"),s);return`${a}${c}${i?`
|
|
31
31
|
${u("gray",E)}`:""}`}default:{const s=i?`${u("cyan",E)} `:"",c=a.split(`
|
|
32
|
-
`).length,d=r?Et(e.output,
|
|
32
|
+
`).length,d=r?Et(e.output,kt,i):i?[u("cyan",G)]:[],h=d.join(`
|
|
33
33
|
`),m=d.length+1;return`${a}${s}${_t({output:e.output,cursor:this.cursor,options:this.options,maxItems:e.maxItems,columnPadding:s.length,rowPadding:c+m,style:(f,g)=>t(f,f.disabled?"disabled":g?"active":"inactive")}).join(`
|
|
34
34
|
${s}`)}
|
|
35
35
|
${h}
|
|
36
|
-
`}}}}).prompt()};`${u("gray",E)}`;const
|
|
37
|
-
`:""}${
|
|
36
|
+
`}}}}).prompt()};`${u("gray",E)}`;const Pe=e=>new dt({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??U.withGuide,r=`${`${t?`${u("gray",E)}
|
|
37
|
+
`:""}${Be(this.state)} `}${e.message}
|
|
38
38
|
`,i=e.placeholder&&e.placeholder.length>0?u("inverse",e.placeholder[0])+u("dim",e.placeholder.slice(1)):u(["inverse","hidden"],"_"),n=this.userInput?this.userInputWithCursor:i,o=this.value??"";switch(this.state){case"validating":{const l=t?`${u("cyan",E)} `:"",a=t?u("cyan",G):"",s=u("dim",n),c=u("dim","Validating...");return`${r}${l}${s}
|
|
39
39
|
${a} ${c}
|
|
40
40
|
`}case"error":{const l=this.error?` ${u("yellow",this.error)}`:"",a=t?`${u("yellow",E)} `:"",s=t?u("yellow",G):"";return`${r.trim()}
|
|
@@ -43,23 +43,23 @@ ${s}${l}
|
|
|
43
43
|
`}case"submit":{const l=o?`${t?" ":""}${u("dim",o)}`:"",a=t?u("gray",E):"";return`${r}${a}${l}`}case"cancel":{const l=o?` ${u(["strikethrough","dim"],o)}`:"",a=t?u("gray",E):"";return`${r}${a}${l}${o.trim()?`
|
|
44
44
|
${a}`:""}`}default:{const l=t?`${u("cyan",E)} `:"",a=t?u("cyan",G):"";return`${r}${l}${n}
|
|
45
45
|
${a}
|
|
46
|
-
`}}}}).prompt();function ge(e){return e==null?[]:Array.isArray(e)?e:[e]}function
|
|
46
|
+
`}}}}).prompt();function ge(e){return e==null?[]:Array.isArray(e)?e:[e]}function Ct(e,t,r,i){var n,o=e[t],l=~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]=o==null?l:Array.isArray(o)?o.concat(l):[o,l]}function Tt(e,t){e=e||[],t=t||{};var r,i,n,o,l,a={_:[]},s=0,c=0,d=0,h=e.length;const m=t.alias!==void 0,f=t.unknown!==void 0,g=t.default!==void 0;if(t.alias=t.alias||{},t.string=ge(t.string),t.boolean=ge(t.boolean),m)for(r in t.alias)for(i=t.alias[r]=ge(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]]||[],c=i.length;c-- >0;)t.boolean.push(i[c]);for(s=t.string.length;s-- >0;)for(i=t.alias[t.string[s]]||[],c=i.length;c-- >0;)t.string.push(i[c]);if(g){for(r in t.default)if(o=typeof t.default[r],i=t.alias[r]=t.alias[r]||[],t[o]!==void 0)for(t[o].push(r),s=0;s<i.length;s++)t[o].push(i[s])}const w=f?Object.keys(t.alias):[];for(s=0;s<h;s++){if(n=e[s],n==="--"){a._=a._.concat(e.slice(++s));break}for(c=0;c<n.length&&n.charCodeAt(c)===45;c++);if(c===0)a._.push(n);else if(n.substring(c,c+3)==="no-"){if(o=n.substring(c+3),f&&!~w.indexOf(o))return t.unknown(n);a[o]=!1}else{for(d=c+1;d<n.length&&n.charCodeAt(d)!==61;d++);for(o=n.substring(c,d),l=n.substring(++d)||s+1===h||(""+e[s+1]).charCodeAt(0)===45||e[++s],i=c===2?[o]:o,d=0;d<i.length;d++){if(o=i[d],f&&!~w.indexOf(o))return t.unknown("-".repeat(c)+o);Ct(a,o,d+1<i.length||l,t)}}}if(g)for(r in t.default)a[r]===void 0&&(a[r]=t.default[r]);if(m)for(r in a)for(i=t.alias[r]||[];i.length>0;)a[i.shift()]=a[r];return a}function Rt(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var ae={exports:{}},Me;function jt(){if(Me)return ae.exports;Me=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=(a,s,c=a)=>d=>{let h=""+d,m=h.indexOf(s,a.length);return~m?a+o(h,s,c,m)+s:a+h+s},o=(a,s,c,d)=>{let h="",m=0;do h+=a.substring(m,d)+c,m=d+s.length,d=a.indexOf(s,m);while(~d);return h+a.substring(m)},l=(a=i)=>{let s=a?n:()=>String;return{isColorSupported:a,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 ae.exports=l(),ae.exports.createColors=l,ae.exports}var At=jt();const Ot=Rt(At),{cyan:Bt,magenta:Lt,yellow:Pt}=Ot,q=[{name:"engine",display:"Engine",description:"with the PlayCanvas Engine API directly",color:Pt},{name:"react",display:"React",description:"with @playcanvas/react",color:Bt},{name:"web-components",display:"Web Components",description:"with @playcanvas/web-components",color:Lt}],Mt={physics:{engine:["assets"],react:["react"],"web-components":["assets"]}},K=[{name:"blank",display:"Blank",category:"Basics",description:"just the skeleton, with a rotating cube and a light"},{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:"first-person-game",display:"First-Person Game",category:"Games",description:"a timed orb hunt across ramps, stairs and jumps",features:["physics"]},{name:"third-person-game",display:"Third-Person Game",category:"Games",description:"an animated character, follow camera and locomotion",features:["physics"]},{name:"platformer",display:"Platformer",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",omit:["src/starter.css","src/assets"],outro:`To publish it to Snapchat, run:
|
|
47
47
|
|
|
48
48
|
npm run package
|
|
49
49
|
|
|
50
|
-
and upload the zip it writes to dist/ at https://my-lenses.snapchat.com/`},{name:"scene-editor",display:"Scene Editor",category:"Tools",description:"click to select, gizmos and a scene saved in the browser"},{name:"vr
|
|
50
|
+
and upload the zip it writes to dist/ at https://my-lenses.snapchat.com/`},{name:"scene-editor",display:"Scene Editor",category:"Tools",description:"click to select, gizmos and a scene saved in the browser"},{name:"vr",display:"Virtual Reality",category:"XR",description:"immersive VR, controllers and locomotion"},{name:"ar",display:"Augmented Reality",category:"XR",description:"mobile AR, hit testing and object placement"}],Nt=async({argFormat:e,yes:t,cancel:r})=>{if(e&&q.some(n=>n.name===e))return e;if(e&&t)throw new Error(`Unknown format: ${e}. Choose from: ${q.map(n=>n.name).join(", ")}`);if(t)return q[0].name;const i=await fe({message:e?`"${e}" isn't a valid format. Please choose from below: `:"Select a format:",options:q.map(n=>({label:n.color(n.display),value:n.name,hint:n.description}))});return Y(i)&&r(),i},Dt=async({argStarter:e,yes:t,cancel:r})=>{if(e&&K.some(n=>n.name===e))return e;if(t){if(!e)return K[0].name;throw new Error(`Unknown starter: ${e}. Choose from: ${K.map(n=>n.name).join(", ")}`)}const i=await fe({message:e?`"${e}" isn't a valid starter. Please choose from below: `:"Select a starter:",initialValue:K[0].name,options:K.map(n=>({label:n.display,value:n.name,hint:`${n.category} \xB7 ${n.description}`}))});return Y(i)&&r(),i},Ne=e=>/^(?:@[a-z\d\-*~][a-z\d\-*._~]*\/)?[a-z\d\-~][a-z\d\-._~]*$/.test(e),be=e=>e.trim().toLowerCase().replace(/\s+/g,"-").replace(/^[._]/,"").replace(/[^a-z\d\-~]+/g,"-"),Wt=async({targetDir:e,yes:t,cancel:r})=>{let i=b.basename(b.resolve(e));if(!Ne(i)){if(t)return be(i);const n=await Pe({message:"Package name:",defaultValue:be(i),placeholder:be(i),validate:o=>{if(!o||!Ne(o))return"Invalid package.json name"}});if(Y(n))return r();i=n}return i},oe=e=>e.trim().replace(/\/+$/g,""),Vt=e=>{const t=y.readdirSync(e);return t.length===0||t.length===1&&t[0]===".git"},Ut=e=>{if(y.existsSync(e))for(const t of y.readdirSync(e))t!==".git"&&y.rmSync(b.resolve(e,t),{recursive:!0,force:!0})},Gt=async({argTargetDir:e,defaultTargetDir:t,yes:r,cancel:i})=>{let n=e?oe(String(e)):void 0;if(!n&&r)return t;if(!n){const o=await Pe({message:"Project name:",defaultValue:t,placeholder:t,validate:l=>{if(l&&!oe(l))return"Invalid project name"}});Y(o)&&i(),n=oe(o)}return n},Kt=async({targetDir:e,argOverwrite:t,argKeep:r,yes:i,cancel:n})=>{if(t&&r)throw new Error("Pass --keep or --overwrite, not both.");if(!y.existsSync(e)||Vt(e))return!1;let o="no";if(t)o="yes";else if(r)o="keep";else{if(i)throw new Error(`Target directory "${e}" is not empty. Pass --keep to add the project to its files, or --overwrite to remove them.`);o=await fe({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:"Keep existing files and continue",value:"keep"}]})}if(Y(o))return n();switch(o){case"yes":return Ut(e),!1;case"keep":if(y.existsSync(b.join(e,"package.json")))throw new Error(`Target directory "${e}" already has a package.json, so it can't keep its files. Pass --overwrite to remove them.`);return!0;default:return n()}},Ht=["scripts","dependencies","devDependencies","peerDependencies","engines"],De={"README.md":"## Prerequisites","AGENTS.md":"## Checking your work"},zt="## Agent skills",Jt=["npm","pnpm","yarn","bun"],Yt=e=>e==="yarn"?{install:"yarn",run:"yarn"}:{install:`${e} install`,run:`${e} run`},qt=e=>{const{cwd:t,targetDir:r,format:i,starter:n,packageName:o,skills:l,keep:a,renameFiles:s}=e,c=b.resolve(t,r);y.mkdirSync(c,{recursive:!0});const d=process.env.npm_config_user_agent?.split("/")[0]??"npm",h=Jt.includes(d)?d:"npm",{install:m,run:f}=Yt(h),g=p=>h==="npm"?p:p.replace(/\bnpm run ([^\s`]+) -- /g,`${f} $1 `).replace(/\bnpm run\b/g,f).replace(/\bnpm install\b/g,m);N.step(`Creating project in ${c}...`);const w=He(import.meta.url),_=b.dirname(w),F=e.templatesDir??[b.resolve(_,"../templates"),b.resolve(_,"../../templates")].find(p=>y.existsSync(b.join(p,i)));if(!F)throw new Error(`Format directory not found for: ${i}`);const j=K.find(p=>p.name===n);if(!j)throw new Error(`Starter not found: ${n}`);const M=b.join(F,"_common"),O=b.join(F,i,"base"),v=b.join(F,"_shared",n),k=b.join(F,i,n);if(!y.existsSync(k))throw new Error(`Starter directory not found: ${i}/${n}`);const $=a?y.mkdtempSync(b.join(Ke.tmpdir(),"create-playcanvas-")):c,I=[M,O,v,...(j.features??[]).flatMap(p=>Mt[p][i].map(C=>b.join(F,"_features",p,C))),k].filter(p=>y.existsSync(p)),R=j.omit??[],D=R.flatMap(p=>[M,O].map(C=>b.join(C,p))),A=Object.keys(De).map(p=>b.join(v,p));for(const p of I){const C=b.join(p,"package.json");y.cpSync(p,$,{recursive:!0,filter:x=>x!==C&&!A.includes(x)&&!D.includes(x)})}for(const[p,C]of Object.entries(De)){const x=b.join($,p);if(!y.existsSync(x))continue;let T=y.readFileSync(x,"utf8");const Q=b.join(v,p);if(y.existsSync(Q)){const J=`${y.readFileSync(Q,"utf8").trimEnd()}
|
|
51
51
|
|
|
52
|
-
${
|
|
52
|
+
${C}`;T=T.replace(C,()=>J)}l||(T=`${T.split(/^(?=## )/m).filter(J=>!J.startsWith(zt)).join("").trimEnd()}
|
|
53
53
|
`),T=T.split(`
|
|
54
54
|
`).filter(J=>!R.some(Ve=>J.startsWith(`- \`${Ve}\``))).join(`
|
|
55
|
-
`),y.writeFileSync(x,g(T))}for(const[p,
|
|
56
|
-
`),l){const p=[b.resolve(_,"skills"),b.resolve(_,"../../node_modules/@playcanvas/skills/skills")].find(x=>y.existsSync(x));if(!p)throw new Error("Skills directory not found");const
|
|
55
|
+
`),y.writeFileSync(x,g(T))}for(const[p,C]of Object.entries(s)){const x=b.join($,p);y.existsSync(x)&&y.renameSync(x,b.join($,C))}const H=I.map(p=>b.join(p,"package.json")).filter(p=>y.existsSync(p)).map(p=>JSON.parse(y.readFileSync(p,"utf-8"))),X=Object.assign({},...H);for(const p of Ht){const C=Object.assign({},...H.map(T=>T[p])),x=Object.entries(C).sort(([T],[Q])=>T>Q?1:-1);x.length?X[p]=Object.fromEntries(x):delete X[p]}if(X.name=o,y.writeFileSync(b.join($,"package.json"),JSON.stringify(X,null,4)+`
|
|
56
|
+
`),l){const p=[b.resolve(_,"skills"),b.resolve(_,"../../node_modules/@playcanvas/skills/skills")].find(x=>y.existsSync(x));if(!p)throw new Error("Skills directory not found");const C=y.readdirSync(p,{withFileTypes:!0}).filter(x=>x.isDirectory()).map(x=>x.name);for(const x of[".claude/skills",".agents/skills"])for(const T of C)y.cpSync(b.join(p,T),b.join($,x,T),{recursive:!0});N.step("Added PlayCanvas agent skills (opt out with --no-skills)")}if(a){const p=[];try{for(const C of y.readdirSync($,{recursive:!0}).sort()){const x=b.join($,C);if(!y.statSync(x).isFile())continue;const T=b.join(c,C);y.existsSync(T)?p.push(C.split(b.sep).join("/")):(y.mkdirSync(b.dirname(T),{recursive:!0}),y.copyFileSync(x,T))}}finally{y.rmSync($,{recursive:!0,force:!0})}p.length&&N.warn(`Kept these existing files rather than the project's: ${p.join(", ")}`)}let z=`Project created. Now run:
|
|
57
57
|
`;const le=b.isAbsolute(r)?c:b.relative(t,c);c!==t&&(z+=`
|
|
58
58
|
cd ${le.includes(" ")?`"${le}"`:le}`),z+=`
|
|
59
59
|
${m}`,z+=`
|
|
60
60
|
${f} dev`,j.outro&&(z+=`
|
|
61
61
|
|
|
62
|
-
${g(j.outro)}`),Ft(z)},
|
|
62
|
+
${g(j.outro)}`),Ft(z)},L=Tt(process.argv.slice(2),{alias:{b:"boilerplate",f:"format",h:"help",s:"starter",t:"template",y:"yes"},boolean:["help","overwrite","keep","yes"],string:["format","starter","template","boilerplate"]}),Xt=process.cwd(),We=e=>e.map((t,r)=>` ${t.name.padEnd(27)}${t.description}${r?"":" (default)"}`).join(`
|
|
63
63
|
`),Qt=`Usage: npm create playcanvas@latest [DIRECTORY] -- [OPTION]...
|
|
64
64
|
|
|
65
65
|
Create a new PlayCanvas project with TypeScript in DIRECTORY.
|
|
@@ -83,4 +83,4 @@ ${We(K)}
|
|
|
83
83
|
|
|
84
84
|
Without a terminal, as when an AI agent or CI runs it, the CLI takes the
|
|
85
85
|
defaults for any option not passed, as --yes does.
|
|
86
|
-
`,Zt={_gitignore:".gitignore",_npmrc:".npmrc"},er="playcanvas-project",tr=async()=>{const e=
|
|
86
|
+
`,Zt={_gitignore:".gitignore",_npmrc:".npmrc"},er="playcanvas-project",tr=async()=>{const e=L._[0]?oe(String(L._[0])):void 0,t=L.format??L.template,r=L.starter??L.boilerplate,i=L.overwrite,n=L.keep,o=!!(process.stdin.isTTY&&process.stdout.isTTY),l=L.yes||!o;if(L.help){console.log(Qt);return}!o&&!L.yes&&N.info("Not running in a terminal, so using the defaults for any option not passed");const a=()=>{It("Operation cancelled"),process.exit(0)},s=await Gt({argTargetDir:e,defaultTargetDir:er,yes:l,cancel:a}),c=await Kt({targetDir:s,argOverwrite:i,argKeep:n,yes:l,cancel:a}),d=await Wt({targetDir:s,yes:l,cancel:a}),h=await Nt({argFormat:t,yes:l,cancel:a}),m=await Dt({argStarter:r,yes:l,cancel:a}),f=L.skills??!0;qt({cwd:Xt,targetDir:s,format:h,starter:m,packageName:d,skills:f,keep:c,renameFiles:Zt})};tr().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.17.0",
|
|
4
4
|
"description": "Scaffold a PlayCanvas project",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"playcanvas",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"eslint": "^9.39.5",
|
|
50
50
|
"mri": "^1.2.0",
|
|
51
51
|
"picocolors": "^1.1.1",
|
|
52
|
-
"prettier": "^3.10.
|
|
52
|
+
"prettier": "^3.10.1",
|
|
53
53
|
"unbuild": "^3.6.1",
|
|
54
54
|
"vite": "^8.3.8",
|
|
55
55
|
"vitest": "^5.1.0"
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
## First-person game
|
|
2
|
+
|
|
3
|
+
This project is a first-person game. `src/game.ts` holds the whole game as one engine script.
|
|
4
|
+
|
|
5
|
+
- The game reads `public/level.glb` by node name: a mesh called `level` to draw, a mesh called `collision` to stand on, nodes called `orb-…` and a node called `spawn`. A replacement level needs the same names. Without a `collision` mesh the game uses `level` for both.
|
|
6
|
+
- The `collision` mesh is never drawn. It is simpler than `level`: stairs are ramps in it, since the player's capsule can't climb steps, and unseen walls above the yard's own keep the player in. The game has no handling for a player who leaves the level.
|
|
7
|
+
- The level's tints are vertex colours, and its base colour texture is a baked ambient occlusion map, so the shading falls on sunlit surfaces as well as shaded ones. Changing the level's shape means baking the map again, or the shading no longer matches.
|
|
8
|
+
- The game is paused by setting `app.timeScale` to 0, which stops physics and every script's `dt`. Anything that should keep moving while paused has to use real time.
|
|
9
|
+
- The engine's first-person controller only reads the keyboard and mouse while the canvas holds the pointer lock. A browser that refuses the lock, such as an embedded preview pane or one driven by automation, can't steer with them, so the game holds the run there and shows its `blocked` card. To play a run in a test there, start it with a `pointerdown` on the canvas whose `pointerType` is `'touch'`, which needs no lock, and move the player by setting the velocity of its rigid body. A synthetic touch event can't steer either, and the controller logs a pointer-capture error for one.
|
|
10
|
+
- The game pauses only when a lock it held is lost, and shows the `blocked` card on `pointerlockerror`. Don't pause on every `pointerlockchange`: a browser that refuses the lock can fire one too.
|
|
11
|
+
- The game recreates the controller in `reset()`, so tune `jumpForce` and `speedGround` there.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
## First-Person Game
|
|
2
|
+
|
|
3
|
+
Orb Run, a small first-person game: collect the five orbs in a walled yard before the 90-second clock runs out. A wall with two doorways divides the yard, and a walkway on pillars runs through a gap in it, up a flight of stairs at each end. Each corner has a terrace, reached by a ramp or by jumping across from the walkway. The game starts on a click or a tap, can be won or lost, and starts again.
|
|
4
|
+
|
|
5
|
+
### Controls
|
|
6
|
+
|
|
7
|
+
- **Keyboard and mouse:** click to play. Move with WASD or the arrow keys, look with the mouse, jump with Space and hold Shift to sprint. Escape gives the mouse back and holds the game, and a click resumes it. The keyboard and mouse only steer while the browser has locked the mouse to the game, so play in an ordinary browser tab: a preview pane embedded in an editor may refuse the lock, and the game says so when that happens.
|
|
8
|
+
- **Touch:** tap to play. Steer with a thumb on the left half of the screen, look by dragging on the right half, and double-tap to jump.
|
|
9
|
+
|
|
10
|
+
### How it fits together
|
|
11
|
+
|
|
12
|
+
- `src/game.ts` is the game, an engine script. It builds the level, gives the player the engine's first-person controller, keeps the state of a run and fires a `game:state` app event for the HUD to follow.
|
|
13
|
+
- `public/level.glb` is the level, put together from a small kit of parts on a grid of 2 m cells: blocks, half blocks, ramps, stairs and decks. Its tints are vertex colours, and its one texture is an ambient occlusion map baked in Blender, which shades every corner and the foot of every wall and step at no cost while the game runs. It also holds a second, simpler mesh that is never drawn, which is what the player stands on: a flight of stairs is a ramp in it, so the player glides up.
|
|
14
|
+
- The HUD is markup, in `index.html` (`src/App.tsx` in React), styled by `src/game.css`. It shows the orbs found, the seconds left, a card for each state and a circle where a thumb is steering.
|
|
15
|
+
|
|
16
|
+
### Make it your own
|
|
17
|
+
|
|
18
|
+
- **Replace the level** with any GLB. The game finds its nodes by name: a mesh called `level` to look at, any number called `orb-…` to collect, and one called `spawn` where the player starts. A mesh called `collision` is what the player stands on and walks into. Leave it out and the player stands on `level` itself, which suits a level with no stairs.
|
|
19
|
+
- **Keep the player in.** This level's collision mesh has unseen walls that rise above the yard's own, so nobody jumps out. An open level needs the same, or somewhere to catch a player who falls off it.
|
|
20
|
+
- **Replace the game** by rewriting `src/game.ts`. Keep its shape: one state, changed in one place, which stops the clock outside play, a `reset()` that the first run goes through too, and a pause when the mouse is given back or the page is hidden. Fire the same `game:state` event, or rework the HUD to follow your own.
|
|
21
|
+
- **Keep** the player and camera in the scene, the sky and the two lights, and the HUD's card and joystick circle. None of them depends on Orb Run.
|
|
Binary file
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/* the orbs found and the seconds left, across the top */
|
|
2
|
+
.status {
|
|
3
|
+
display: flex;
|
|
4
|
+
justify-content: center;
|
|
5
|
+
gap: 24px;
|
|
6
|
+
font-size: 22px;
|
|
7
|
+
font-weight: 700;
|
|
8
|
+
font-variant-numeric: tabular-nums;
|
|
9
|
+
text-shadow: 0 1px 6px #000a;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/* the card sits over the middle of the view, and lets a click on it through to the canvas, where
|
|
13
|
+
the game is listening */
|
|
14
|
+
.card {
|
|
15
|
+
position: absolute;
|
|
16
|
+
top: 50%;
|
|
17
|
+
left: 50%;
|
|
18
|
+
translate: -50% -50%;
|
|
19
|
+
text-align: center;
|
|
20
|
+
pointer-events: none;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/* the crosshair is for aiming, so it goes while a card is up */
|
|
24
|
+
.card:not([hidden]) ~ .crosshair {
|
|
25
|
+
display: none;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/* the circle shows where a thumb went down on the left of the screen, and the knob follows it. The
|
|
29
|
+
circle's 70px radius is how far the controller lets the knob travel */
|
|
30
|
+
.joystick,
|
|
31
|
+
.joystick-knob {
|
|
32
|
+
position: fixed;
|
|
33
|
+
border-radius: 50%;
|
|
34
|
+
translate: -50% -50%;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.joystick {
|
|
38
|
+
width: 140px;
|
|
39
|
+
height: 140px;
|
|
40
|
+
border: 2px solid #fff6;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.joystick-knob {
|
|
44
|
+
width: 56px;
|
|
45
|
+
height: 56px;
|
|
46
|
+
background: #fff6;
|
|
47
|
+
}
|