create-playcanvas 0.11.1 → 0.12.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 +8 -4
- package/dist/index.mjs +38 -39
- package/package.json +1 -1
- package/templates/_common/CLAUDE.md +1 -0
- package/templates/_shared/web-lens/AGENTS.md +10 -0
- package/templates/engine/base/AGENTS.md +37 -0
- package/templates/engine/base/README.md +14 -10
- package/templates/engine/base/package.json +1 -1
- package/templates/engine/base/vite.config.ts +5 -0
- package/templates/engine/web-lens/vite.config.ts +4 -1
- package/templates/react/base/AGENTS.md +41 -0
- package/templates/react/base/README.md +14 -10
- package/templates/react/base/package.json +1 -1
- package/templates/react/base/vite.config.ts +5 -0
- package/templates/react/web-lens/vite.config.ts +4 -1
- package/templates/web-components/base/AGENTS.md +39 -0
- package/templates/web-components/base/README.md +14 -10
- package/templates/web-components/base/package.json +2 -2
- package/templates/web-components/base/vite.config.ts +5 -0
- package/templates/web-components/web-lens/vite.config.ts +4 -1
package/README.md
CHANGED
|
@@ -71,9 +71,11 @@ npm create playcanvas@latest my-game -- -f react -y
|
|
|
71
71
|
|
|
72
72
|
`--yes` takes `playcanvas-project`, the `engine` format and the `spinning-cube` starter for anything you don't pass. It never deletes files, so a non-empty target directory still needs `--overwrite`. A run without a terminal, such as one started by an AI agent or in CI, takes the same defaults, so it never waits on a prompt. The previous `--template` and `--boilerplate` long flags remain accepted for compatibility.
|
|
73
73
|
|
|
74
|
-
##
|
|
74
|
+
## Coding agents
|
|
75
75
|
|
|
76
|
-
Every project includes
|
|
76
|
+
Every project includes an `AGENTS.md` that gives AI coding agents such as Claude Code, Codex and Cursor its commands, layout and conventions, and how to check a change. Its `CLAUDE.md` imports it, since Claude Code skips `AGENTS.md` when a `CLAUDE.md` exists in the project or a folder above it.
|
|
77
|
+
|
|
78
|
+
Every project also includes [`@playcanvas/skills`](https://github.com/playcanvas/skills), so those agents pick up PlayCanvas-specific workflows automatically, with no install step. They are copied into `.claude/skills/` and `.agents/skills/`. Pass `--no-skills` to leave them out.
|
|
77
79
|
|
|
78
80
|
## Development
|
|
79
81
|
|
|
@@ -91,13 +93,15 @@ Contributions are welcome. Please open an issue before proposing a new format, s
|
|
|
91
93
|
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
94
|
|
|
93
95
|
1. `_common/`: files every project gets
|
|
94
|
-
2. `<format>/base/`: the format's tooling and
|
|
95
|
-
3. `_shared/<starter>/`: the starter's format-independent code and assets. Its `README.md` is inserted into the base README above `## Prerequisites`
|
|
96
|
+
2. `<format>/base/`: the format's tooling, README and `AGENTS.md`, a default `index.html` for Engine and React, and React's `src/main.tsx`
|
|
97
|
+
3. `_shared/<starter>/`: the starter's format-independent code and assets. Its `README.md` is inserted into the base README above `## Prerequisites`, and an optional `AGENTS.md`, for what an agent must know about the starter, into the base `AGENTS.md` above `## Checking your work`
|
|
96
98
|
4. `_features/<feature>/<layer>/`: what each of the starter's `features` needs in that format, as mapped by `FEATURES` in `templates/index.ts`
|
|
97
99
|
5. `<format>/<starter>/`: the starter's scene in that format
|
|
98
100
|
|
|
99
101
|
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
102
|
|
|
103
|
+
Write commands in a README or `AGENTS.md` for npm, as `npm install`, `npm run <script>` and `npm run <script> -- <args>`. The scaffolder rewrites them for the package manager that created the project, and leaves out the `## Agent skills` section of both when run with `--no-skills`.
|
|
104
|
+
|
|
101
105
|
## License
|
|
102
106
|
|
|
103
107
|
[MIT](LICENSE)
|
package/dist/index.mjs
CHANGED
|
@@ -1,57 +1,56 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{styleText as c}from"node:util";import
|
|
3
|
-
`);let
|
|
4
|
-
`){l&&(i+=
|
|
5
|
-
`&&(n&&
|
|
6
|
-
`)}function
|
|
2
|
+
import{styleText as c}from"node:util";import j,{stdin as je,stdout as le}from"node:process";import Oe from"node:readline";import"node:tty";import w from"node:fs";import $ from"node:path";import{fileURLToPath as Le}from"node:url";const Pe=(()=>{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,Me=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,Ne=/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]|\u001b\]8;[^;]*;.*?(?:\u0007|\u001b\u005c)/y,De=/[\x00-\x08\x0A-\x1F\x7F-\x9F]{1,1000}/y,ce=/(?:(?![\uFF61-\uFF9F\uFF00-\uFFEF])[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}\p{Script=Tangut}]){1,1000}/yu,We=/\t{1,1000}/y,ue=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"),Ve=/(?:[\x20-\x7E\xA0-\xFF](?!\uFE0F)){1,1000}/y,Ue=new RegExp("\\p{M}+","gu"),Ge={limit:1/0,ellipsis:""},he=(e,t={},r={})=>{const i=t.limit??1/0,n=t.ellipsis??"",l=t?.ellipsisWidth??(n?he(n,Ge,r).width:0),a=0,o=r.controlWidth??0,s=r.tabWidth??8,u=r.emojiWidth??2,m=2,h=r.regularWidth??1,d=r.wideWidth??m,y=[[Ve,h],[Ne,a],[De,o],[We,s],[ue,u],[ce,d]];let f=0,b=0,F=e.length,T=0,R=!1,L=F,k=Math.max(0,i-l),v=0,S=0,x=0,E=0;e:for(;;){if(S>v||b>=F&&b>f){const p=e.slice(v,S)||e.slice(f,b);T=0;for(const _ of p.replaceAll(Ue,"")){const g=_.codePointAt(0)||0;if(Be(g)?E=m:Me(g)?E=d:E=h,x+E>k&&(L=Math.min(L,Math.max(v,f)+T)),x+E>i){R=!0;break e}T+=_.length,x+=E}v=S=0}if(b>=F)break e;for(let p=0,_=y.length;p<_;p++){const[g,C]=y[p];if(g.lastIndex=b,g.test(e)){if(T=g===ce?Pe(e.slice(b,g.lastIndex)):g===ue?1:g.lastIndex-b,E=T*C,x+E>k&&(L=Math.min(L,b+Math.floor((k-x)/C))),x+E>i){R=!0;break e}x+=E,v=f,S=b,b=f=g.lastIndex;continue e}}b+=1}return{width:R?k:x,index:R?L:F,truncated:R,ellipsed:R&&i>=l}},Ke={limit:1/0,ellipsis:"",ellipsisWidth:0},M=(e,t={})=>he(e,Ke,t).width,G="\x1B",me="\x9B",He=39,Q="\x07",de="[",ze="]",pe="m",ee=`${ze}8;;`,fe=new RegExp(`(?:\\${de}(?<code>\\d+)m|\\${ee}(?<uri>.*)${Q})`,"y"),ge=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},be=e=>`${G}${de}${e}${pe}`,ye=e=>`${G}${ee}${e}${Q}`,te=(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(),m=0;for(;!s.done;){const h=s.value,d=M(h);o+d<=r?e[e.length-1]+=h:(e.push(h),o=0),(h===G||h===me)&&(n=!0,l=t.startsWith(ee,m+1)),n?l?h===Q&&(n=!1,l=!1):h===pe&&(n=!1):(o+=d,o===r&&!u.done&&(e.push(""),o=0)),s=u,u=i.next(),m+=h.length}a=e.at(-1),!o&&a!==void 0&&a.length&&e.length>1&&(e[e.length-2]+=e.pop())},Je=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("")},Ye=(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 d=a[h];if(r.trim!==!1){const f=o.at(-1)??"",b=f.trimStart();f.length!==b.length&&(o[o.length-1]=b,s=M(b))}h!==0&&(s>=t&&(r.wordWrap===!1||r.trim===!1)&&(o.push(""),s=0),(s||r.trim===!1)&&(o[o.length-1]+=" ",s++));const y=M(d);if(r.hard&&y>t){const f=t-s,b=1+Math.floor((y-f-1)/t);Math.floor((y-1)/t)<b&&o.push(""),te(o,d,t),s=M(o.at(-1)??"");continue}if(s+y>t&&s&&y){if(r.wordWrap===!1&&s<t){te(o,d,t),s=M(o.at(-1)??"");continue}o.push(""),s=0}if(s+y>t&&r.wordWrap===!1){te(o,d,t),s=M(o.at(-1)??"");continue}o[o.length-1]+=d,s+=y}r.trim!==!1&&(o=o.map(h=>Je(h)));const u=o.join(`
|
|
3
|
+
`);let m=!1;for(let h=0;h<u.length;h++){const d=u[h];if(i+=d,m)m=!1;else if(m=d>="\uD800"&&d<="\uDBFF",m)continue;if(d===G||d===me){fe.lastIndex=h+1;const f=fe.exec(u)?.groups;if(f?.code!==void 0){const b=Number.parseFloat(f.code);n=b===He?void 0:b}else f?.uri!==void 0&&(l=f.uri.length===0?void 0:f.uri)}if(u[h+1]===`
|
|
4
|
+
`){l&&(i+=ye(""));const y=n?ge(n):void 0;n&&y&&(i+=be(y))}else d===`
|
|
5
|
+
`&&(n&&ge(n)&&(i+=be(n)),l&&(i+=ye(l)))}return i},qe=/\r?\n/;function K(e,t,r){return String(e).normalize().split(qe).map(i=>Ye(i,t,r)).join(`
|
|
6
|
+
`)}function Xe(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var re,$e;function Ze(){if($e)return re;$e=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 re={cursor:i,scroll:n,erase:l,beep:r},re}var P=Ze();function H(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?H(l,t<0?-1:1,r):l}const Qe=["up","down","left","right","space","enter","cancel"],N={actions:new Set(Qe),aliases:new Map([["k","up"],["j","down"],["h","left"],["l","right"],["","cancel"],["escape","cancel"]]),withGuide:!0};function et(e){if(e!==void 0)return e;const t=process.env.ACCESSIBLE;return t!==void 0&&t!==""&&t!=="0"&&t!=="false"}function xe(e,t){if(typeof e=="string")return N.aliases.get(e)===t;for(const r of e)if(r!==void 0&&xe(r,t))return!0;return!1}function tt(e,t){if(e===t)return;const r=e.split(`
|
|
7
7
|
`),i=t.split(`
|
|
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
|
|
9
|
-
`).map((o,s,u)=>{const
|
|
10
|
-
`)}function
|
|
11
|
-
`),
|
|
12
|
-
`).length-1;this.output.write(
|
|
13
|
-
`);this.output.write(o[a]),this._prevFrame=t,this.output.write(
|
|
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 se=Symbol("clack:cancel");function V(e){return e===se}function z(e,t){const r=e;r.isTTY&&r.setRawMode(t)}const we=e=>"columns"in e&&typeof e.columns=="number"?e.columns:80,ve=e=>"rows"in e&&typeof e.rows=="number"?e.rows:20;function ie(e,t,r,i=r,n=r,l){const a=we(e??le);return K(t,a-r.length,{hard:!0,trim:!1}).split(`
|
|
9
|
+
`).map((o,s,u)=>{const m=o;return s===0?`${i}${m}`:s===u.length-1?`${n}${m}`:`${r}${m}`}).join(`
|
|
10
|
+
`)}function rt(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 Se{input;output;_abortSignal;rl;opts;_render;_track=!1;_prevFrame="";_subscribers=new Map;_cursor=0;state="initial";error="";value;userInput="";get accessible(){return et(this.opts.accessible)}constructor(t,r=!0){const{input:i=je,output:n=le,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(se);this._abortSignal.addEventListener("abort",()=>{this.state="cancel",this.close()},{once:!0})}this.rl=Oe.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),z(this.input,!0),this.output.on("resize",this.render),this.render(),this.once("submit",()=>{this.output.write(P.cursor.show),this.output.off("resize",this.render),z(this.input,!1),t(this.value)}),this.once("cancel",()=>{this.output.write(P.cursor.show),this.output.off("resize",this.render),z(this.input,!1),t(se)})})}_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&&N.aliases.has(r.name)&&this.emit("cursor",N.aliases.get(r.name)),N.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=rt(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")}xe([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
|
+
`),z(this.input,!1),this.rl?.close(),this.rl=void 0,this.emit(`${this.state}`,this.value),this.unsubscribe()}restoreCursor(){const t=K(this._prevFrame,process.stdout.columns,{hard:!0,trim:!1}).split(`
|
|
12
|
+
`).length-1;this.output.write(P.cursor.move(-999,t*-1))}render(){const t=K(this._render(this)??"",process.stdout.columns,{hard:!0,trim:!1});if(t!==this._prevFrame){if(this.state==="initial")this.output.write(P.cursor.hide);else{const r=tt(this._prevFrame,t),i=ve(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(P.cursor.move(0,a-l)),this.output.write(P.erase.lines(1));const o=t.split(`
|
|
13
|
+
`);this.output.write(o[a]),this._prevFrame=t,this.output.write(P.cursor.move(0,o.length-a-1));return}else if(r.lines.length>1){if(n<l)a=n;else{const s=a-l;s>0&&this.output.write(P.cursor.move(0,s))}this.output.write(P.erase.down());const o=t.split(`
|
|
14
14
|
`).slice(a);this.output.write(o.join(`
|
|
15
|
-
`)),this._prevFrame=t;return}}this.output.write(
|
|
16
|
-
`);
|
|
17
|
-
`);if(h.length>0){const[
|
|
15
|
+
`)),this._prevFrame=t;return}}this.output.write(P.erase.down())}this.output.write(t),this.state==="initial"&&(this.state="active"),this._prevFrame=t}}}let st=class extends Se{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?H(i,1,this.options):i,this.changeValue(),this.on("cursor",n=>{switch(n){case"left":case"up":this.cursor=H(this.cursor,-1,this.options);break;case"down":case"right":this.cursor=H(this.cursor,1,this.options);break}this.changeValue()})}};class it extends Se{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 nt(){return j.platform!=="win32"?j.env.TERM!=="linux":!!j.env.CI||!!j.env.WT_SESSION||!!j.env.TERMINUS_SUBLIME||j.env.ConEmuTask==="{cmd::Cmder}"||j.env.TERM_PROGRAM==="Terminus-Sublime"||j.env.TERM_PROGRAM==="vscode"||j.env.TERM==="xterm-256color"||j.env.TERM==="alacritty"||j.env.TERMINAL_EMULATOR==="JetBrains-JediTerm"}const at=nt(),A=(e,t)=>at?e:t,Ee=A("\u25C6","*"),ot=A("\u25A0","x"),lt=A("\u25B2","x"),_e=A("\u25C7","o"),I=A("\u2502","|"),D=A("\u2514","\u2014"),ct=A("\u25CF",">"),Ie=A("\u25CB"," "),ut=A("\u25CF","\u2022"),ht=A("\u25C6","*"),mt=A("\u25B2","!"),dt=A("\u25A0","x"),Fe=e=>{switch(e){case"initial":case"active":return c("cyan",Ee);case"cancel":return c("red",ot);case"error":return c("yellow",lt);case"submit":return c("green",_e);case"validating":return c("dim",Ee)}},pt=e=>{switch(e){case"initial":case"active":return c("cyan",I);case"cancel":return c("red",I);case"error":return c("yellow",I);case"submit":return c("green",I)}};function ft(e,t){const r=[`${t?`${c("cyan",I)} `:""}${e.join(" \u2022 ")}`];return t&&r.push(c("cyan",D)),r}const Ce=(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}},gt=({cursor:e,options:t,style:r,output:i=process.stdout,maxItems:n=Number.POSITIVE_INFINITY,columnPadding:l=0,rowPadding:a=4})=>{const o=we(i)-l,s=ve(i),u=c("dim","..."),m=Math.max(s-a,0),h=Math.max(Math.min(n,m),5);let d=0;e>=h-3&&(d=Math.max(Math.min(e-h+3,t.length-h),0));let y=h<t.length&&d>0,f=h<t.length&&d+h<t.length;const b=Math.min(d+h,t.length),F=[];let T=0;y&&T++,f&&T++;const R=d+(y?1:0),L=b-(f?1:0);for(let v=R;v<L;v++){const S=t[v],x=S?r(S,v===e):"",E=K(x,o,{hard:!0,trim:!1}).split(`
|
|
16
|
+
`);F.push(E),T+=E.length}if(T>m){let v=0,S=0,x=T;const E=e-R;let p=m;const _=()=>Ce(F,x,0,E,p),g=()=>Ce(F,x,E+1,F.length,p,!0);y?({lineCount:x,removals:v}=_(),x>p&&(f||(p-=1),{lineCount:x,removals:S}=g())):(f||(p-=1),{lineCount:x,removals:S}=g(),x>p&&(p-=1,{lineCount:x,removals:v}=_())),v>0&&(y=!0,F.splice(0,v)),S>0&&(f=!0,F.splice(F.length-S,S))}const k=[];y&&k.push(u);for(const v of F)for(const S of v)k.push(S);return f&&k.push(u),k};`${c("dim","\u2191/\u2193")}`,`${c("dim","Space:")}`,`${c("dim","Enter:")}`;const B={message:(e=[],{symbol:t=c("gray",I),secondarySymbol:r=c("gray",I),output:i=process.stdout,spacing:n=1,withGuide:l}={})=>{const a=[],o=l??N.withGuide,s=o?r:"",u=o?`${t} `:"",m=o?`${r} `:"";for(let d=0;d<n;d++)a.push(s);const h=Array.isArray(e)?e:e.split(`
|
|
17
|
+
`);if(h.length>0){const[d,...y]=h;d.length>0?a.push(`${u}${d}`):a.push(o?t:"");for(const f of y)f.length>0?a.push(`${m}${f}`):a.push(o?r:"")}i.write(`${a.join(`
|
|
18
18
|
`)}
|
|
19
|
-
`)},info:(e,t)=>{
|
|
19
|
+
`)},info:(e,t)=>{B.message(e,{...t,symbol:c("blue",ut)})},success:(e,t)=>{B.message(e,{...t,symbol:c("green",ht)})},step:(e,t)=>{B.message(e,{...t,symbol:c("green",_e)})},warn:(e,t)=>{B.message(e,{...t,symbol:c("yellow",mt)})},warning:(e,t)=>{B.warn(e,t)},error:(e,t)=>{B.message(e,{...t,symbol:c("red",dt)})}},bt=(e="",t)=>{const r=process.stdout,i=`${c("gray",D)} `;r.write(`${i}${c("red",e)}
|
|
20
20
|
|
|
21
|
-
`)},
|
|
22
|
-
${c("gray",
|
|
21
|
+
`)},yt=(e="",t)=>{const r=process.stdout,i=`${c("gray",I)}
|
|
22
|
+
${c("gray",D)} `;r.write(`${i}${e}
|
|
23
23
|
|
|
24
|
-
`)}
|
|
24
|
+
`)},$t=[`${c("dim","\u2191/\u2193")} to navigate`,`${c("dim","Enter:")} confirm`],J=(e,t)=>e.includes(`
|
|
25
25
|
`)?e.split(`
|
|
26
26
|
`).map(r=>t(r)).join(`
|
|
27
|
-
`):t(e),
|
|
27
|
+
`):t(e),ne=e=>{const t=(i,n)=>{if(i===void 0)return"";const l=i.label??String(i.value);switch(n){case"disabled":return`${c("gray",Ie)} ${J(l,a=>c("gray",a))}${i.hint?` ${c("dim",`(${i.hint??"disabled"})`)}`:""}`;case"selected":return`${J(l,a=>c("dim",a))}`;case"active":return`${c("green",ct)} ${l}${i.hint?` ${c("dim",`(${i.hint})`)}`:""}`;case"cancelled":return`${J(l,a=>c(["strikethrough","dim"],a))}`;default:return`${c("dim",Ie)} ${J(l,a=>c("dim",a))}`}},r=e.showInstructions??!0;return new st({options:e.options,signal:e.signal,input:e.input,output:e.output,initialValue:e.initialValue,render(){const i=e.withGuide??N.withGuide,n=`${Fe(this.state)} `,l=`${pt(this.state)} `,a=ie(e.output,e.message,l,n),o=`${i?`${c("gray",I)}
|
|
28
28
|
`:""}${a}
|
|
29
|
-
`;switch(this.state){case"submit":{const s=i?`${c("gray",
|
|
30
|
-
${c("gray",
|
|
31
|
-
`).length,
|
|
32
|
-
`),m
|
|
29
|
+
`;switch(this.state){case"submit":{const s=i?`${c("gray",I)} `:"",u=ie(e.output,t(this.options[this.cursor],"selected"),s);return`${o}${u}`}case"cancel":{const s=i?`${c("gray",I)} `:"",u=ie(e.output,t(this.options[this.cursor],"cancelled"),s);return`${o}${u}${i?`
|
|
30
|
+
${c("gray",I)}`:""}`}default:{const s=i?`${c("cyan",I)} `:"",u=o.split(`
|
|
31
|
+
`).length,m=r?ft($t,i):i?[c("cyan",D)]:[],h=m.join(`
|
|
32
|
+
`),d=m.length+1;return`${o}${s}${gt({output:e.output,cursor:this.cursor,options:this.options,maxItems:e.maxItems,columnPadding:s.length,rowPadding:u+d,style:(y,f)=>t(y,y.disabled?"disabled":f?"active":"inactive")}).join(`
|
|
33
33
|
${s}`)}
|
|
34
34
|
${h}
|
|
35
|
-
`}}}}).prompt()};`${c("gray",
|
|
36
|
-
`:""}${
|
|
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",
|
|
35
|
+
`}}}}).prompt()};`${c("gray",I)}`;const Te=e=>new it({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??N.withGuide,r=`${`${t?`${c("gray",I)}
|
|
36
|
+
`:""}${Fe(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",I)} `:"",o=t?c("cyan",D):"",s=c("dim",n),u=c("dim","Validating...");return`${r}${a}${s}
|
|
38
38
|
${o} ${u}
|
|
39
|
-
`}case"error":{const a=this.error?` ${c("yellow",this.error)}`:"",o=t?`${c("yellow",
|
|
39
|
+
`}case"error":{const a=this.error?` ${c("yellow",this.error)}`:"",o=t?`${c("yellow",I)} `:"",s=t?c("yellow",D):"";return`${r.trim()}
|
|
40
40
|
${o}${n}
|
|
41
41
|
${s}${a}
|
|
42
|
-
`}case"submit":{const a=l?`${t?" ":""}${c("dim",l)}`:"",o=t?c("gray",
|
|
43
|
-
${o}`:""}`}default:{const a=t?`${c("cyan",
|
|
42
|
+
`}case"submit":{const a=l?`${t?" ":""}${c("dim",l)}`:"",o=t?c("gray",I):"";return`${r}${o}${a}`}case"cancel":{const a=l?` ${c(["strikethrough","dim"],l)}`:"",o=t?c("gray",I):"";return`${r}${o}${a}${l.trim()?`
|
|
43
|
+
${o}`:""}`}default:{const a=t?`${c("cyan",I)} `:"",o=t?c("cyan",D):"";return`${r}${a}${n}
|
|
44
44
|
${o}
|
|
45
|
-
`}}}}).prompt();function
|
|
45
|
+
`}}}}).prompt();function ae(e){return e==null?[]:Array.isArray(e)?e:[e]}function xt(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 wt(e,t){e=e||[],t=t||{};var r,i,n,l,a,o={_:[]},s=0,u=0,m=0,h=e.length;const d=t.alias!==void 0,y=t.unknown!==void 0,f=t.default!==void 0;if(t.alias=t.alias||{},t.string=ae(t.string),t.boolean=ae(t.boolean),d)for(r in t.alias)for(i=t.alias[r]=ae(t.alias[r]),s=0;s<i.length;s++)(t.alias[i[s]]=i.concat(r)).splice(s,1);for(s=t.boolean.length;s-- >0;)for(i=t.alias[t.boolean[s]]||[],u=i.length;u-- >0;)t.boolean.push(i[u]);for(s=t.string.length;s-- >0;)for(i=t.alias[t.string[s]]||[],u=i.length;u-- >0;)t.string.push(i[u]);if(f){for(r in t.default)if(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=y?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),y&&!~b.indexOf(l))return t.unknown(n);o[l]=!1}else{for(m=u+1;m<n.length&&n.charCodeAt(m)!==61;m++);for(l=n.substring(u,m),a=n.substring(++m)||s+1===h||(""+e[s+1]).charCodeAt(0)===45||e[++s],i=u===2?[l]:l,m=0;m<i.length;m++){if(l=i[m],y&&!~b.indexOf(l))return t.unknown("-".repeat(u)+l);xt(o,l,m+1<i.length||a,t)}}}if(f)for(r in t.default)o[r]===void 0&&(o[r]=t.default[r]);if(d)for(r in o)for(i=t.alias[r]||[];i.length>0;)o[i.shift()]=o[r];return o}var Y={exports:{}},Re;function vt(){if(Re)return Y.exports;Re=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)=>m=>{let h=""+m,d=h.indexOf(s,o.length);return~d?o+l(h,s,u,d)+s:o+h+s},l=(o,s,u,m)=>{let h="",d=0;do h+=o.substring(d,m)+u,d=m+s.length,m=o.indexOf(s,d);while(~m);return h+o.substring(d)},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 Y.exports=a(),Y.exports.createColors=a,Y.exports}var St=vt();const Et=Xe(St),{cyan:_t,magenta:It,yellow:Ft}=Et,q=[{name:"engine",display:"Engine",description:"with the PlayCanvas Engine API directly",color:Ft},{name:"react",display:"React",description:"with @playcanvas/react",color:_t},{name:"web-components",display:"Web Components",description:"with @playcanvas/web-components",color:It}],Ct={physics:{engine:["assets"],react:["react"],"web-components":["assets"]}},W=[{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"}],Tt=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 ne({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 V(i)&&r(),i},Rt=async({argStarter:e,yes:t,cancel:r})=>{if(e&&W.some(n=>n.name===e))return e;if(t){if(!e)return W[0].name;throw new Error(`Unknown starter: ${e}. Choose from: ${W.map(n=>n.name).join(", ")}`)}const i=await ne({message:e?`"${e}" isn't a valid starter. Please choose from below: `:"Select a starter:",initialValue:W[0].name,options:W.map(n=>({label:n.display,value:n.name,hint:`${n.category} \xB7 ${n.description}`}))});return V(i)&&r(),i},Ae=e=>/^(?:@[a-z\d\-*~][a-z\d\-*._~]*\/)?[a-z\d\-~][a-z\d\-._~]*$/.test(e),oe=e=>e.trim().toLowerCase().replace(/\s+/g,"-").replace(/^[._]/,"").replace(/[^a-z\d\-~]+/g,"-"),At=async({targetDir:e,yes:t,cancel:r})=>{let i=$.basename($.resolve(e));if(!Ae(i)){if(t)return oe(i);const n=await Te({message:"Package name:",defaultValue:oe(i),placeholder:oe(i),validate:l=>{if(!l||!Ae(l))return"Invalid package.json name"}});if(V(n))return r();i=n}return i},X=e=>e.trim().replace(/\/+$/g,""),kt=e=>{const t=w.readdirSync(e);return t.length===0||t.length===1&&t[0]===".git"},jt=e=>{if(w.existsSync(e))for(const t of w.readdirSync(e))t!==".git"&&w.rmSync($.resolve(e,t),{recursive:!0,force:!0})},Ot=async({argTargetDir:e,defaultTargetDir:t,yes:r,cancel:i})=>{let n=e?X(String(e)):void 0;if(!n&&r)return t;if(!n){const l=await Te({message:"Project name:",defaultValue:t,placeholder:t,validate:a=>{if(a&&!X(a))return"Invalid project name"}});V(l)&&i(),n=X(l)}return n},Lt=async({targetDir:e,argOverwrite:t,yes:r,cancel:i})=>{if(w.existsSync(e)&&!kt(e)){if(r&&!t)throw new Error(`Target directory "${e}" is not empty. Pass --overwrite to remove its contents.`);const n=t?"yes":await ne({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(V(n))return i();switch(n){case"yes":jt(e);break;case"no":i();return}}},Pt=["scripts","dependencies","devDependencies","peerDependencies","engines"],ke={"README.md":"## Prerequisites","AGENTS.md":"## Checking your work"},Bt="## Agent skills",Mt=["npm","pnpm","yarn","bun"],Nt=e=>e==="yarn"?{install:"yarn",run:"yarn"}:{install:`${e} install`,run:`${e} run`},Dt=e=>{const{cwd:t,targetDir:r,format:i,starter:n,packageName:l,skills:a,renameFiles:o}=e,s=$.resolve(t,r);w.mkdirSync(s,{recursive:!0});const u=process.env.npm_config_user_agent?.split("/")[0]??"npm",m=Mt.includes(u)?u:"npm",{install:h,run:d}=Nt(m);B.step(`Creating project in ${s}...`);const y=Le(import.meta.url),f=$.dirname(y),b=e.templatesDir??[$.resolve(f,"../templates"),$.resolve(f,"../../templates")].find(p=>w.existsSync($.join(p,i)));if(!b)throw new Error(`Format directory not found for: ${i}`);const F=W.find(p=>p.name===n);if(!F)throw new Error(`Starter not found: ${n}`);const T=$.join(b,"_shared",n),R=$.join(b,i,n);if(!w.existsSync(R))throw new Error(`Starter directory not found: ${i}/${n}`);const L=[$.join(b,"_common"),$.join(b,i,"base"),T,...(F.features??[]).flatMap(p=>Ct[p][i].map(_=>$.join(b,"_features",p,_))),R].filter(p=>w.existsSync(p)),k=Object.keys(ke).map(p=>$.join(T,p));for(const p of L){const _=$.join(p,"package.json");w.cpSync(p,s,{recursive:!0,filter:g=>g!==_&&!k.includes(g)})}for(const[p,_]of Object.entries(ke)){const g=$.join(s,p);if(!w.existsSync(g))continue;let C=w.readFileSync(g,"utf8");const U=$.join(T,p);if(w.existsSync(U)){const Z=`${w.readFileSync(U,"utf8").trimEnd()}
|
|
46
46
|
|
|
47
|
-
${
|
|
48
|
-
`),
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
${
|
|
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]...
|
|
47
|
+
${_}`;C=C.replace(_,()=>Z)}a||(C=`${C.split(/^(?=## )/m).filter(Z=>!Z.startsWith(Bt)).join("").trimEnd()}
|
|
48
|
+
`),m!=="npm"&&(C=C.replace(/\bnpm run ([^\s`]+) -- /g,`${d} $1 `).replace(/\bnpm run\b/g,d).replace(/\bnpm install\b/g,h)),w.writeFileSync(g,C)}for(const[p,_]of Object.entries(o)){const g=$.join(s,p);w.existsSync(g)&&w.renameSync(g,$.join(s,_))}const v=L.map(p=>$.join(p,"package.json")).filter(p=>w.existsSync(p)).map(p=>JSON.parse(w.readFileSync(p,"utf-8"))),S=Object.assign({},...v);for(const p of Pt){const _=Object.assign({},...v.map(C=>C[p])),g=Object.entries(_).sort(([C],[U])=>C>U?1:-1);g.length?S[p]=Object.fromEntries(g):delete S[p]}if(S.name=l,w.writeFileSync($.join(s,"package.json"),JSON.stringify(S,null,4)+`
|
|
49
|
+
`),a){const p=[$.resolve(f,"../skills"),$.resolve(f,"../../skills")].find(g=>w.existsSync(g));if(!p)throw new Error("Skills directory not found");const _=w.readdirSync(p,{withFileTypes:!0}).filter(g=>g.isDirectory()).map(g=>g.name);for(const g of[".claude/skills",".agents/skills"])for(const C of _)w.cpSync($.join(p,C),$.join(s,g,C),{recursive:!0});B.step("Added PlayCanvas agent skills (opt out with --no-skills)")}let x=`Project created. Now run:
|
|
50
|
+
`;const E=$.isAbsolute(r)?s:$.relative(t,s);s!==t&&(x+=`
|
|
51
|
+
cd ${E.includes(" ")?`"${E}"`:E}`),x+=`
|
|
52
|
+
${h}`,x+=`
|
|
53
|
+
${d} dev`,yt(x)},O=wt(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"]}),Wt=process.cwd(),Vt=`Usage: npm create playcanvas@latest [OPTION]...
|
|
55
54
|
|
|
56
55
|
Create a new PlayCanvas project with TypeScript.
|
|
57
56
|
With no arguments, start the CLI in interactive mode.
|
|
@@ -66,4 +65,4 @@ Options:
|
|
|
66
65
|
|
|
67
66
|
Without a terminal, as when an AI agent or CI runs it, the CLI takes the
|
|
68
67
|
defaults for any option not passed, as --yes does.
|
|
69
|
-
`,
|
|
68
|
+
`,Ut={_gitignore:".gitignore",_npmrc:".npmrc"},Gt="playcanvas-project",Kt=async()=>{const e=O._[0]?X(String(O._[0])):void 0,t=O.format??O.template,r=O.starter??O.boilerplate,i=O.overwrite,n=!!(process.stdin.isTTY&&process.stdout.isTTY),l=O.yes||!n;if(O.help){console.log(Vt);return}!n&&!O.yes&&B.info("Not running in a terminal, so using the defaults for any option not passed");const a=()=>{bt("Operation cancelled"),process.exit(0)},o=await Ot({argTargetDir:e,defaultTargetDir:Gt,yes:l,cancel:a});await Lt({targetDir:o,argOverwrite:i,yes:l,cancel:a});const s=await At({targetDir:o,yes:l,cancel:a}),u=await Tt({argFormat:t,yes:l,cancel:a}),m=await Rt({argStarter:r,yes:l,cancel:a}),h=O.skills??!0;Dt({cwd:Wt,targetDir:o,format:u,starter:m,packageName:s,skills:h,renameFiles:Ut})};Kt().catch(e=>{console.error(e instanceof Error?e.message:e),process.exitCode=1});
|
package/package.json
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@AGENTS.md
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
## Snapchat Web Lens
|
|
2
|
+
|
|
3
|
+
This project is a Snapchat Web Lens: Snapchat runs it in a sandbox, from an uploaded zip.
|
|
4
|
+
|
|
5
|
+
- Run `npm run package` after a change. It builds into `dist/lens/`, checks the build against the Web Lens upload rules and writes the zip to `dist/`, or reports why it wrote none.
|
|
6
|
+
- A Lens can't reach the network. Bundle every asset into the build, and load nothing from another host, such as a script from a CDN, a web font or a remote model.
|
|
7
|
+
- The zip must stay under 8 MB and `index.html` under 1 MB.
|
|
8
|
+
- `npm run start` serves the build under the Lens sandbox's rules, so a request that would fail in Snapchat also fails in the browser console.
|
|
9
|
+
- `src/lens.ts` is the only code that talks to Snapchat. Outside Snapchat, the Web Lens API answers from a stub with canned data, so the game runs in any browser. Call `recordScore(score)` when a run ends.
|
|
10
|
+
- Keep the layout portrait and the HUD inside the safe area, using the `--inset-*` properties from `src/lens.css`.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
A PlayCanvas Engine app in TypeScript, built with Vite. The scene is built in code with the `playcanvas` package's API. `README.md` describes the starter this project began from and how to control it.
|
|
4
|
+
|
|
5
|
+
## Commands
|
|
6
|
+
|
|
7
|
+
- `npm install` installs dependencies.
|
|
8
|
+
- `npm run dev` starts the dev server, usually at <http://localhost:5173>, with hot reload and the PlayCanvas Inspector.
|
|
9
|
+
- `npm run typecheck` checks types.
|
|
10
|
+
- `npm run lint` runs ESLint, and `npm run lint:fix` fixes what it can.
|
|
11
|
+
- `npm run fmt` checks formatting, and `npm run fmt:fix` applies Prettier.
|
|
12
|
+
- `npm run build` builds the production site into `dist/`, and `npm run start` serves it.
|
|
13
|
+
|
|
14
|
+
## Layout
|
|
15
|
+
|
|
16
|
+
- `index.html` is the page, with the `<canvas id="application-canvas">` the app draws into.
|
|
17
|
+
- `src/main.ts` is the entry point. It creates the graphics device and the app, then the scene.
|
|
18
|
+
- `src/starter.css` styles the page and the starter's panels.
|
|
19
|
+
- `src/inspector.ts` loads the Inspector. `vite.config.ts` adds it in the dev server only, so production builds leave it out.
|
|
20
|
+
- `public/` holds files copied into the build as they are, such as models and textures.
|
|
21
|
+
|
|
22
|
+
## Conventions
|
|
23
|
+
|
|
24
|
+
- The app is an `AppBase` given only the component systems and resource handlers it uses, which keeps the bundle small. To use another component or asset type, add its system to `componentSystems`, such as `SoundComponentSystem` for `addComponent('sound')`, or its handler to `resourceHandlers`, such as `AudioHandler` for audio assets. Without its system, `addComponent()` returns `null`, and the default build logs no error.
|
|
25
|
+
- Builds use relative URLs (`base: './'` in `vite.config.ts`), so they run from any folder. Load files in `public/` with relative paths such as `./model.glb`, not `/model.glb`.
|
|
26
|
+
- The engine ships ready-made scripts, such as camera controls and character controllers, under `playcanvas/scripts/esm/`. Use one before writing the same behavior yourself.
|
|
27
|
+
- `node_modules/playcanvas/build/playcanvas.d.ts` documents the installed engine's API.
|
|
28
|
+
- TypeScript is strict, and unused locals and parameters are errors.
|
|
29
|
+
|
|
30
|
+
## Checking your work
|
|
31
|
+
|
|
32
|
+
- Run `npm run typecheck`, `npm run lint` and `npm run build` before you finish. All three must pass.
|
|
33
|
+
- To see the result, run `npm run dev` and open the page in a browser. The console should show `Powered by PlayCanvas` and no errors. Press the backquote key for the Inspector, which shows the hierarchy, assets, materials, textures, frame graph and physics. <kbd>F9</kbd> pauses and <kbd>F10</kbd> steps a frame.
|
|
34
|
+
|
|
35
|
+
## Agent skills
|
|
36
|
+
|
|
37
|
+
`.claude/skills/` and `.agents/skills/` hold PlayCanvas skills for coding agents. Use them rather than working from memory: `build-app` when restructuring the app, `find-examples` for a feature the project doesn't use yet, `reuse-scripts` before writing a behavior, `inspect-glb` before placing a model, `inspect-runtime` to read state from the running app, and `verify-pixels` to prove a refactor leaves the image unchanged.
|
|
@@ -9,8 +9,6 @@ Node.js 22.23.2 or later.
|
|
|
9
9
|
## Getting started
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
npm create playcanvas@latest playcanvas-project -- --format engine
|
|
13
|
-
cd playcanvas-project
|
|
14
12
|
npm install
|
|
15
13
|
npm run dev
|
|
16
14
|
```
|
|
@@ -23,17 +21,23 @@ Open <http://localhost:5173>. Edit the files under `src/` and save to see the sc
|
|
|
23
21
|
|
|
24
22
|
## Scripts
|
|
25
23
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
|
29
|
-
|
|
|
30
|
-
| `
|
|
31
|
-
| `
|
|
32
|
-
| `
|
|
33
|
-
| `
|
|
24
|
+
Run these with `npm run <script>`:
|
|
25
|
+
|
|
26
|
+
| Script | Description |
|
|
27
|
+
| ----------- | --------------------------------- |
|
|
28
|
+
| `dev` | Start the Vite development server |
|
|
29
|
+
| `build` | Build for production |
|
|
30
|
+
| `start` | Preview the production build |
|
|
31
|
+
| `lint` | Run ESLint |
|
|
32
|
+
| `fmt` | Check formatting |
|
|
33
|
+
| `typecheck` | Run TypeScript checks |
|
|
34
34
|
|
|
35
35
|
Run `npm run build` to generate a deployable static site in `dist/`. Its paths are relative, so it runs from any folder, such as a GitHub Pages project site or an itch.io upload.
|
|
36
36
|
|
|
37
|
+
## Coding agents
|
|
38
|
+
|
|
39
|
+
`AGENTS.md` gives coding agents such as Claude Code, Codex and Cursor this project's commands, layout, conventions and checks. `CLAUDE.md` imports it for Claude Code.
|
|
40
|
+
|
|
37
41
|
## Agent skills
|
|
38
42
|
|
|
39
43
|
This project includes [`@playcanvas/skills`](https://github.com/playcanvas/skills) under `.claude/skills/` and `.agents/skills/`, so Claude Code, Codex and Cursor pick up PlayCanvas-specific workflows automatically.
|
|
@@ -5,6 +5,11 @@ export default defineConfig({
|
|
|
5
5
|
// Relative URLs, so the build runs wherever it's served from: a domain root, a subpath such as
|
|
6
6
|
// GitHub Pages or itch.io, or a Snapchat Web Lens
|
|
7
7
|
base: './',
|
|
8
|
+
build: {
|
|
9
|
+
// A PlayCanvas build is 1-2.5 MB minified (300-600 kB gzipped), nearly all of it engine code the
|
|
10
|
+
// first frame needs, so warn only for chunks well past that rather than at Vite's 500 kB default
|
|
11
|
+
chunkSizeWarningLimit: 3000
|
|
12
|
+
},
|
|
8
13
|
plugins: [
|
|
9
14
|
{
|
|
10
15
|
// Dev only: install the PlayCanvas Inspector before the app starts (press ` to toggle)
|
|
@@ -19,7 +19,10 @@ export default defineConfig({
|
|
|
19
19
|
// npm run package zips this folder and writes the zip beside it, inside the ignored dist/
|
|
20
20
|
outDir: 'dist/lens',
|
|
21
21
|
// source maps only add to the 8 MB upload
|
|
22
|
-
sourcemap: false
|
|
22
|
+
sourcemap: false,
|
|
23
|
+
// a PlayCanvas build is 1-2.5 MB minified, nearly all of it engine code the first frame needs,
|
|
24
|
+
// so warn only for chunks well past that rather than at Vite's 500 kB default
|
|
25
|
+
chunkSizeWarningLimit: 3000
|
|
23
26
|
},
|
|
24
27
|
preview: {
|
|
25
28
|
// npm run start serves the build under the Lens rules, so a request for anything outside
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
A PlayCanvas app in TypeScript and React, built with Vite. The scene is declared in JSX with `@playcanvas/react`, which drives the PlayCanvas Engine. `README.md` describes the starter this project began from and how to control it.
|
|
4
|
+
|
|
5
|
+
## Commands
|
|
6
|
+
|
|
7
|
+
- `npm install` installs dependencies.
|
|
8
|
+
- `npm run dev` starts the dev server, usually at <http://localhost:5173>, with hot reload and the PlayCanvas Inspector.
|
|
9
|
+
- `npm run typecheck` checks types.
|
|
10
|
+
- `npm run lint` runs ESLint, and `npm run lint:fix` fixes what it can.
|
|
11
|
+
- `npm run fmt` checks formatting, and `npm run fmt:fix` applies Prettier.
|
|
12
|
+
- `npm run build` builds the production site into `dist/`, and `npm run start` serves it.
|
|
13
|
+
|
|
14
|
+
## Layout
|
|
15
|
+
|
|
16
|
+
- `index.html` is the page, with the `#root` element React renders into.
|
|
17
|
+
- `src/main.tsx` mounts `<App />` inside `<StrictMode>`.
|
|
18
|
+
- `src/App.tsx` holds the scene: an `<Application>` of `<Entity>` elements, with components such as `<Camera>`, `<Light>` and `<Render>` inside them.
|
|
19
|
+
- `src/starter.css` styles the page and the starter's panels.
|
|
20
|
+
- `src/inspector.ts` loads the Inspector. `vite.config.ts` adds it in the dev server only, so production builds leave it out.
|
|
21
|
+
- `public/` holds files copied into the build as they are, such as models and textures.
|
|
22
|
+
|
|
23
|
+
## Conventions
|
|
24
|
+
|
|
25
|
+
- Import `<Application>` and `<Entity>` from `@playcanvas/react`, components from `@playcanvas/react/components` and hooks such as `useApp`, `useAppEvent` and `useModel` from `@playcanvas/react/hooks`. Engine classes and types come from `playcanvas`.
|
|
26
|
+
- Reach an entity's engine object through a `ref`, typed with `Entity` from `playcanvas`.
|
|
27
|
+
- Do per-frame work in `useAppEvent('update', (dt) => ...)` on engine objects, not in React state, which would re-render every frame.
|
|
28
|
+
- In development, `<StrictMode>` mounts each component twice, so anything an effect adds to the engine, such as an entity, a script or an event listener, needs a cleanup that removes it.
|
|
29
|
+
- Builds use relative URLs (`base: './'` in `vite.config.ts`), so they run from any folder. Load files in `public/` with relative paths such as `./model.glb`, not `/model.glb`.
|
|
30
|
+
- The engine ships ready-made scripts, such as camera controls and character controllers, under `playcanvas/scripts/esm/`. Use one before writing the same behavior yourself.
|
|
31
|
+
- `node_modules/playcanvas/build/playcanvas.d.ts` documents the installed engine's API.
|
|
32
|
+
- TypeScript is strict, and unused locals and parameters are errors.
|
|
33
|
+
|
|
34
|
+
## Checking your work
|
|
35
|
+
|
|
36
|
+
- Run `npm run typecheck`, `npm run lint` and `npm run build` before you finish. All three must pass.
|
|
37
|
+
- To see the result, run `npm run dev` and open the page in a browser. The console should show `Powered by PlayCanvas` and no errors. Press the backquote key for the Inspector, which shows the hierarchy, assets, materials, textures, frame graph and physics. <kbd>F9</kbd> pauses and <kbd>F10</kbd> steps a frame.
|
|
38
|
+
|
|
39
|
+
## Agent skills
|
|
40
|
+
|
|
41
|
+
`.claude/skills/` and `.agents/skills/` hold PlayCanvas skills for coding agents. Use them rather than working from memory: `build-app` when restructuring the app, `find-examples` for a feature the project doesn't use yet, `reuse-scripts` before writing a behavior, `inspect-glb` before placing a model, `inspect-runtime` to read state from the running app, and `verify-pixels` to prove a refactor leaves the image unchanged.
|
|
@@ -9,8 +9,6 @@ Node.js 22.23.2 or later.
|
|
|
9
9
|
## Getting started
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
npm create playcanvas@latest playcanvas-project -- --format react
|
|
13
|
-
cd playcanvas-project
|
|
14
12
|
npm install
|
|
15
13
|
npm run dev
|
|
16
14
|
```
|
|
@@ -23,17 +21,23 @@ Open <http://localhost:5173>. Edit the files under `src/` and save to see the sc
|
|
|
23
21
|
|
|
24
22
|
## Scripts
|
|
25
23
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
|
29
|
-
|
|
|
30
|
-
| `
|
|
31
|
-
| `
|
|
32
|
-
| `
|
|
33
|
-
| `
|
|
24
|
+
Run these with `npm run <script>`:
|
|
25
|
+
|
|
26
|
+
| Script | Description |
|
|
27
|
+
| ----------- | --------------------------------- |
|
|
28
|
+
| `dev` | Start the Vite development server |
|
|
29
|
+
| `build` | Build for production |
|
|
30
|
+
| `start` | Preview the production build |
|
|
31
|
+
| `lint` | Run ESLint |
|
|
32
|
+
| `fmt` | Check formatting |
|
|
33
|
+
| `typecheck` | Run TypeScript checks |
|
|
34
34
|
|
|
35
35
|
Run `npm run build` to generate a deployable static site in `dist/`. Its paths are relative, so it runs from any folder, such as a GitHub Pages project site or an itch.io upload.
|
|
36
36
|
|
|
37
|
+
## Coding agents
|
|
38
|
+
|
|
39
|
+
`AGENTS.md` gives coding agents such as Claude Code, Codex and Cursor this project's commands, layout, conventions and checks. `CLAUDE.md` imports it for Claude Code.
|
|
40
|
+
|
|
37
41
|
## Agent skills
|
|
38
42
|
|
|
39
43
|
This project includes [`@playcanvas/skills`](https://github.com/playcanvas/skills) under `.claude/skills/` and `.agents/skills/`, so Claude Code, Codex and Cursor pick up PlayCanvas-specific workflows automatically.
|
|
@@ -6,6 +6,11 @@ export default defineConfig({
|
|
|
6
6
|
// Relative URLs, so the build runs wherever it's served from: a domain root, a subpath such as
|
|
7
7
|
// GitHub Pages or itch.io, or a Snapchat Web Lens
|
|
8
8
|
base: './',
|
|
9
|
+
build: {
|
|
10
|
+
// A PlayCanvas build is 1-2.5 MB minified (300-600 kB gzipped), nearly all of it engine code the
|
|
11
|
+
// first frame needs, so warn only for chunks well past that rather than at Vite's 500 kB default
|
|
12
|
+
chunkSizeWarningLimit: 3000
|
|
13
|
+
},
|
|
9
14
|
plugins: [
|
|
10
15
|
react(),
|
|
11
16
|
{
|
|
@@ -20,7 +20,10 @@ export default defineConfig({
|
|
|
20
20
|
// npm run package zips this folder and writes the zip beside it, inside the ignored dist/
|
|
21
21
|
outDir: 'dist/lens',
|
|
22
22
|
// source maps only add to the 8 MB upload
|
|
23
|
-
sourcemap: false
|
|
23
|
+
sourcemap: false,
|
|
24
|
+
// a PlayCanvas build is 1-2.5 MB minified, nearly all of it engine code the first frame needs,
|
|
25
|
+
// so warn only for chunks well past that rather than at Vite's 500 kB default
|
|
26
|
+
chunkSizeWarningLimit: 3000
|
|
24
27
|
},
|
|
25
28
|
preview: {
|
|
26
29
|
// npm run start serves the build under the Lens rules, so a request for anything outside
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
A PlayCanvas app in TypeScript, built with Vite. The scene is declared in HTML with `@playcanvas/web-components`, custom elements that drive the PlayCanvas Engine. `README.md` describes the starter this project began from and how to control it.
|
|
4
|
+
|
|
5
|
+
## Commands
|
|
6
|
+
|
|
7
|
+
- `npm install` installs dependencies.
|
|
8
|
+
- `npm run dev` starts the dev server, usually at <http://localhost:5173>, with hot reload and the PlayCanvas Inspector.
|
|
9
|
+
- `npm run typecheck` checks types.
|
|
10
|
+
- `npm run lint` runs ESLint, and `npm run lint:fix` fixes what it can.
|
|
11
|
+
- `npm run fmt` checks formatting, and `npm run fmt:fix` applies Prettier.
|
|
12
|
+
- `npm run build` builds the production site into `dist/`, and `npm run start` serves it.
|
|
13
|
+
|
|
14
|
+
## Layout
|
|
15
|
+
|
|
16
|
+
- `index.html` holds the scene: `<pc-app>` wraps a `<pc-scene>` of `<pc-entity>` elements, with components such as `<pc-camera>`, `<pc-light>` and `<pc-render>` inside them.
|
|
17
|
+
- `src/main.ts` does what HTML can't, such as attaching scripts and handling input.
|
|
18
|
+
- `src/starter.css` styles the page and the starter's panels.
|
|
19
|
+
- `src/inspector.ts` loads the Inspector. `vite.config.ts` adds it in the dev server only, so production builds leave it out.
|
|
20
|
+
- `public/` holds files copied into the build as they are, such as models and textures.
|
|
21
|
+
|
|
22
|
+
## Conventions
|
|
23
|
+
|
|
24
|
+
- Attribute values are strings, such as `position="0 0 3"` and `clear-color="#8099e6"`. `node_modules/@playcanvas/web-components/dist/custom-elements.json` lists every element and its attributes.
|
|
25
|
+
- Attach a script that's imported through the bundler in `src/main.ts`, not with `<pc-script-instance name="...">`, which only finds scripts that a `<pc-asset>` fetched at runtime. Wait for the element with `whenReady()`, then add the script through its `.entity`.
|
|
26
|
+
- `<pc-app>` dispatches ordinary pointer events, such as `pointerup`, on the `<pc-entity>` under the pointer, so listen for them on the element. Only entities with a listener receive them.
|
|
27
|
+
- Builds use relative URLs (`base: './'` in `vite.config.ts`), so they run from any folder. Load files in `public/` with relative paths such as `./model.glb`, not `/model.glb`.
|
|
28
|
+
- The engine ships ready-made scripts, such as camera controls and character controllers, under `playcanvas/scripts/esm/`. Use one before writing the same behavior yourself.
|
|
29
|
+
- `node_modules/playcanvas/build/playcanvas.d.ts` documents the installed engine's API.
|
|
30
|
+
- TypeScript is strict, and unused locals and parameters are errors.
|
|
31
|
+
|
|
32
|
+
## Checking your work
|
|
33
|
+
|
|
34
|
+
- Run `npm run typecheck`, `npm run lint` and `npm run build` before you finish. All three must pass.
|
|
35
|
+
- To see the result, run `npm run dev` and open the page in a browser. The console should show `Powered by PlayCanvas` and no errors. Press the backquote key for the Inspector, which shows the hierarchy, assets, materials, textures, frame graph and physics. <kbd>F9</kbd> pauses and <kbd>F10</kbd> steps a frame.
|
|
36
|
+
|
|
37
|
+
## Agent skills
|
|
38
|
+
|
|
39
|
+
`.claude/skills/` and `.agents/skills/` hold PlayCanvas skills for coding agents. Use them rather than working from memory: `build-app` when restructuring the app, `find-examples` for a feature the project doesn't use yet, `reuse-scripts` before writing a behavior, `inspect-glb` before placing a model, `inspect-runtime` to read state from the running app, and `verify-pixels` to prove a refactor leaves the image unchanged.
|
|
@@ -10,8 +10,6 @@ Node.js 22.23.2 or later.
|
|
|
10
10
|
## Getting started
|
|
11
11
|
|
|
12
12
|
```bash
|
|
13
|
-
npm create playcanvas@latest playcanvas-project -- --format web-components
|
|
14
|
-
cd playcanvas-project
|
|
15
13
|
npm install
|
|
16
14
|
npm run dev
|
|
17
15
|
```
|
|
@@ -24,14 +22,16 @@ Open <http://localhost:5173>. Edit `index.html` to change the scene.
|
|
|
24
22
|
|
|
25
23
|
## Scripts
|
|
26
24
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
|
30
|
-
|
|
|
31
|
-
| `
|
|
32
|
-
| `
|
|
33
|
-
| `
|
|
34
|
-
| `
|
|
25
|
+
Run these with `npm run <script>`:
|
|
26
|
+
|
|
27
|
+
| Script | Description |
|
|
28
|
+
| ----------- | --------------------------------- |
|
|
29
|
+
| `dev` | Start the Vite development server |
|
|
30
|
+
| `build` | Build for production |
|
|
31
|
+
| `start` | Preview the production build |
|
|
32
|
+
| `lint` | Run ESLint |
|
|
33
|
+
| `fmt` | Check formatting |
|
|
34
|
+
| `typecheck` | Run TypeScript checks |
|
|
35
35
|
|
|
36
36
|
Run `npm run build` to generate a deployable static site in `dist/`. Its paths are relative, so it runs from any folder, such as a GitHub Pages project site or an itch.io upload.
|
|
37
37
|
|
|
@@ -62,6 +62,10 @@ JetBrains IDEs pick it up automatically. For VS Code, add to `.vscode/settings.j
|
|
|
62
62
|
}
|
|
63
63
|
```
|
|
64
64
|
|
|
65
|
+
## Coding agents
|
|
66
|
+
|
|
67
|
+
`AGENTS.md` gives coding agents such as Claude Code, Codex and Cursor this project's commands, layout, conventions and checks. `CLAUDE.md` imports it for Claude Code.
|
|
68
|
+
|
|
65
69
|
## Agent skills
|
|
66
70
|
|
|
67
71
|
This project includes [`@playcanvas/skills`](https://github.com/playcanvas/skills) under `.claude/skills/` and `.agents/skills/`, so Claude Code, Codex and Cursor pick up PlayCanvas-specific workflows automatically.
|
|
@@ -15,12 +15,12 @@
|
|
|
15
15
|
"typecheck": "tsc --noEmit"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@playcanvas/web-components": "^0.28.
|
|
18
|
+
"@playcanvas/web-components": "^0.28.1",
|
|
19
19
|
"playcanvas": "^2.23.3"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"@playcanvas/eslint-config": "3.0.0-beta.8",
|
|
23
|
-
"@playcanvas/inspector": "^0.
|
|
23
|
+
"@playcanvas/inspector": "^0.4.0",
|
|
24
24
|
"eslint": "^9.39.5",
|
|
25
25
|
"prettier": "^3.9.10",
|
|
26
26
|
"typescript": "~6.0.3",
|
|
@@ -5,6 +5,11 @@ export default defineConfig({
|
|
|
5
5
|
// Relative URLs, so the build runs wherever it's served from: a domain root, a subpath such as
|
|
6
6
|
// GitHub Pages or itch.io, or a Snapchat Web Lens
|
|
7
7
|
base: './',
|
|
8
|
+
build: {
|
|
9
|
+
// A PlayCanvas build is 1-2.5 MB minified (300-600 kB gzipped), nearly all of it engine code the
|
|
10
|
+
// first frame needs, so warn only for chunks well past that rather than at Vite's 500 kB default
|
|
11
|
+
chunkSizeWarningLimit: 3000
|
|
12
|
+
},
|
|
8
13
|
plugins: [
|
|
9
14
|
{
|
|
10
15
|
// Dev only: install the PlayCanvas Inspector before the app starts (press ` to toggle)
|
|
@@ -19,7 +19,10 @@ export default defineConfig({
|
|
|
19
19
|
// npm run package zips this folder and writes the zip beside it, inside the ignored dist/
|
|
20
20
|
outDir: 'dist/lens',
|
|
21
21
|
// source maps only add to the 8 MB upload
|
|
22
|
-
sourcemap: false
|
|
22
|
+
sourcemap: false,
|
|
23
|
+
// a PlayCanvas build is 1-2.5 MB minified, nearly all of it engine code the first frame needs,
|
|
24
|
+
// so warn only for chunks well past that rather than at Vite's 500 kB default
|
|
25
|
+
chunkSizeWarningLimit: 3000
|
|
23
26
|
},
|
|
24
27
|
preview: {
|
|
25
28
|
// npm run start serves the build under the Lens rules, so a request for anything outside
|