create-playcanvas 0.12.0 → 0.14.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 +1 -1
- package/package.json +3 -3
- package/skills/add-effects/SKILL.md +2 -1
- package/skills/find-examples/SKILL.md +6 -5
- package/skills/inspect-glb/SKILL.md +2 -1
- package/skills/inspect-runtime/SKILL.md +10 -9
- package/skills/load-assets/SKILL.md +7 -2
- package/skills/override-shader-chunks/SKILL.md +1 -1
- package/templates/_shared/web-lens/AGENTS.md +3 -0
- package/templates/_shared/web-lens/README.md +2 -1
- package/templates/_shared/web-lens/scripts/package-lens.mjs +52 -0
- package/templates/engine/base/AGENTS.md +1 -0
- package/templates/engine/base/README.md +9 -9
- package/templates/engine/base/package.json +5 -5
- package/templates/engine/web-lens/vite.config.ts +18 -2
- package/templates/react/base/AGENTS.md +1 -0
- package/templates/react/base/README.md +9 -9
- package/templates/react/base/eslint.config.mjs +1 -12
- package/templates/react/base/package.json +6 -6
- package/templates/react/web-lens/vite.config.ts +18 -2
- package/templates/web-components/base/AGENTS.md +1 -0
- package/templates/web-components/base/README.md +9 -9
- package/templates/web-components/base/package.json +6 -6
- package/templates/web-components/web-lens/vite.config.ts +18 -2
package/README.md
CHANGED
|
@@ -50,7 +50,7 @@ The `web-lens` starter is a portrait game ready to publish to Snapchat as a [Web
|
|
|
50
50
|
npm create playcanvas@latest my-lens -- --starter web-lens
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
-
Its `npm run package` script builds the game, checks it against the Web Lens upload rules (size limits, relative paths and no
|
|
53
|
+
Its `npm run package` script builds the game, checks it against the Web Lens upload rules (size limits, relative paths, no network access and no camera or microphone) and writes the zip to upload through [My Lenses](https://my-lenses.snapchat.com/).
|
|
54
54
|
|
|
55
55
|
## CLI Options
|
|
56
56
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-playcanvas",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "Scaffold a PlayCanvas project",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"playcanvas",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@clack/prompts": "^1.8.1",
|
|
47
|
-
"@playcanvas/eslint-config": "3.0.0
|
|
47
|
+
"@playcanvas/eslint-config": "3.0.0",
|
|
48
48
|
"@types/node": "22.20.5",
|
|
49
49
|
"eslint": "^9.39.5",
|
|
50
50
|
"mri": "^1.2.0",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"prettier": "^3.9.10",
|
|
53
53
|
"unbuild": "^3.6.1",
|
|
54
54
|
"vite": "^8.3.5",
|
|
55
|
-
"vitest": "^5.0
|
|
55
|
+
"vitest": "^5.1.0"
|
|
56
56
|
},
|
|
57
57
|
"engines": {
|
|
58
58
|
"node": ">=22.23.2"
|
|
@@ -8,7 +8,8 @@ description: Use when adding visual effects to a PlayCanvas app, to choose betwe
|
|
|
8
8
|
Pick the Engine building block first, then adapt its closest official example with
|
|
9
9
|
`find-examples`:
|
|
10
10
|
|
|
11
|
-
- Particle systems: the `particlesystem` component (`graphics/particles-*`
|
|
11
|
+
- Particle systems: the `particlesystem` component (`particles/*` examples; `graphics/particles-*`
|
|
12
|
+
before 2.24).
|
|
12
13
|
- Custom shading on existing surfaces: `StandardMaterial` chunk overrides
|
|
13
14
|
(`override-shader-chunks`), or a `ShaderMaterial` when the effect needs its own shader pair.
|
|
14
15
|
- Procedural or per-frame geometry: a runtime-updated `Mesh` on a `render` component
|
|
@@ -15,17 +15,18 @@ it locally. The npm package omits `examples/`, so otherwise fetch from the insta
|
|
|
15
15
|
|
|
16
16
|
```sh
|
|
17
17
|
TAG="v$(node -p "JSON.parse(require('fs').readFileSync('node_modules/playcanvas/package.json')).version")" || exit 1
|
|
18
|
-
gh api "repos/playcanvas/engine/git/trees/$TAG?recursive=1" | grep -o '"examples/src/examples/[^"]
|
|
19
|
-
gh api "repos/playcanvas/engine/contents
|
|
20
|
-
--jq .content | base64 --decode
|
|
18
|
+
gh api "repos/playcanvas/engine/git/trees/$TAG?recursive=1" | grep -o '"examples/src/examples/[^"]*example\.mjs"'
|
|
19
|
+
gh api "repos/playcanvas/engine/contents/<listed path>?ref=$TAG" --jq .content | base64 --decode
|
|
21
20
|
```
|
|
22
21
|
|
|
23
22
|
Read the resolved package, not the dependency list; `playcanvas` is often transitive. An empty list
|
|
24
|
-
means an invalid tag: stop rather than guess. Use `main` only when the version has no tag.
|
|
23
|
+
means an invalid tag: stop rather than guess. Use `main` only when the version has no tag. From
|
|
24
|
+
2.24, each Engine example is a folder with the shaders and other files it imports; earlier tags use
|
|
25
|
+
`<name>.example.mjs` with sibling `<name>.*` files.
|
|
25
26
|
|
|
26
27
|
| Surface | Repository | Ref | Example paths |
|
|
27
28
|
| --- | --- | --- | --- |
|
|
28
|
-
| direct Engine | `playcanvas/engine` | `v<version>` | `examples/src/examples/<category>/<name
|
|
29
|
+
| direct Engine | `playcanvas/engine` | `v<version>` | `examples/src/examples/<category>/<name>/example.mjs` |
|
|
29
30
|
| Web Components | `playcanvas/web-components` | `v<version>` | `examples/<name>.html` |
|
|
30
31
|
| React | `playcanvas/developer-site` | `main` | `docs/user-manual/react/examples/<name>.mdx` |
|
|
31
32
|
|
|
@@ -37,4 +37,5 @@ transforms:
|
|
|
37
37
|
npx --yes @gltf-transform/cli@4.4.2 copy in.glb scratch.glb
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
-
The Engine
|
|
40
|
+
The Engine loads Meshopt files only from 2.24, with a decoder; see `load-assets` before shipping
|
|
41
|
+
them.
|
|
@@ -16,10 +16,8 @@ From Engine 2.23, every app announces itself to a hook at
|
|
|
16
16
|
- Page code then reads `globalThis.__playcanvasApps`, a `Map` from each live app to
|
|
17
17
|
`{ version, revision, protocol }`. `app.init()` adds the entry and `app.destroy()` removes it.
|
|
18
18
|
- The script chains a hook defined before it, such as a devtools extension's.
|
|
19
|
-
- No entry means the Engine predates 2.23, the hook loaded late, or
|
|
20
|
-
`devtools: false`. The Engine swallows errors
|
|
21
|
-
- In 2.23.0 only the debug build (`playcanvas/debug`, `build/playcanvas.dbg.*`) announces apps; the
|
|
22
|
-
default import never calls the hook. Later 2.23 patches fix every build.
|
|
19
|
+
- No entry means the Engine predates 2.23, is a non-debug 2.23.0 build, the hook loaded late, or
|
|
20
|
+
the app set `devtools: false`. The Engine swallows hook errors.
|
|
23
21
|
|
|
24
22
|
## Read state
|
|
25
23
|
|
|
@@ -34,8 +32,11 @@ From Engine 2.23, every app announces itself to a hook at
|
|
|
34
32
|
|
|
35
33
|
## Inspector panel
|
|
36
34
|
|
|
37
|
-
`@playcanvas/inspector`
|
|
38
|
-
|
|
39
|
-
own `playcanvas` package, so add it only to development builds. It draws over the
|
|
40
|
-
applies view overrides, so destroy it before `verify-pixels` captures. Stepping through
|
|
41
|
-
needs the debug build.
|
|
35
|
+
`@playcanvas/inspector` is a debug panel: `new Inspector(app)` shows the hierarchy, assets, GPU
|
|
36
|
+
resources, frame graph, physics, and frame timings; `record: true` records from creation. It must
|
|
37
|
+
import the app's own `playcanvas` package, so add it only to development builds. It draws over the
|
|
38
|
+
canvas and applies view overrides, so destroy it before `verify-pixels` captures. Stepping through
|
|
39
|
+
draws needs the debug build.
|
|
40
|
+
|
|
41
|
+
With `mcp: true` (0.5.0+), `await inspector.executeTool(name, args)` runs the tools `getTools()`
|
|
42
|
+
lists: find entities, get objects by `$id`, record frames, take screenshots, and read logs.
|
|
@@ -15,8 +15,9 @@ and `EXT_mesh_gpu_instancing`; do not rebuild these by hand. Preload asset sets
|
|
|
15
15
|
|
|
16
16
|
## Compress for size
|
|
17
17
|
|
|
18
|
-
- Geometry: Draco (`KHR_draco_mesh_compression`)
|
|
19
|
-
|
|
18
|
+
- Geometry: Draco (`KHR_draco_mesh_compression`), or from Engine 2.24 Meshopt
|
|
19
|
+
(`EXT_meshopt_compression`, `KHR_meshopt_compression`). Older Engines cannot load Meshopt, so
|
|
20
|
+
decode such files before shipping.
|
|
20
21
|
- Textures: KTX2 with Basis (`KHR_texture_basisu`) transcodes to a GPU format, cutting both
|
|
21
22
|
download size and VRAM. ETC1S is smallest and lossy. UASTC is larger and keeps
|
|
22
23
|
detail, which matters most for normal maps. Weigh size against visible detail.
|
|
@@ -42,6 +43,10 @@ basisInitialize({
|
|
|
42
43
|
});
|
|
43
44
|
```
|
|
44
45
|
|
|
46
|
+
From 2.24, also copy `meshopt/` and call
|
|
47
|
+
`meshoptInitialize({ url: '/wasm/meshopt/meshopt_decoder.mjs' })` before loading Meshopt files;
|
|
48
|
+
without it they fail unless they carry uncompressed fallback buffers.
|
|
49
|
+
|
|
45
50
|
In Web Components, a `<pc-wasm name="DracoDecoderModule">` element under `<pc-app>` sets the Draco
|
|
46
51
|
config with `glue`, `wasm`, and `fallback` attributes.
|
|
47
52
|
|
|
@@ -8,7 +8,7 @@ description: Use when a PlayCanvas StandardMaterial needs custom shading its pro
|
|
|
8
8
|
Overriding a `StandardMaterial` chunk keeps fog, tone mapping, shadows, skinning, and instancing
|
|
9
9
|
intact. Use a `ShaderMaterial` only when the look needs its own vertex and fragment pair.
|
|
10
10
|
|
|
11
|
-
- Set `material.shaderChunksVersion` to the installed major.minor, such as `'2.
|
|
11
|
+
- Set `material.shaderChunksVersion` to the installed major.minor, such as `'2.24'`. Chunk names
|
|
12
12
|
and contracts change between versions, so read the installed chunk source, not memory.
|
|
13
13
|
- Set chunks with `material.getShaderChunks(SHADERLANGUAGE_GLSL).set(name, source)` and the WGSL
|
|
14
14
|
equivalent, then call `material.update()`. For every material on a device, use
|
|
@@ -5,6 +5,9 @@ This project is a Snapchat Web Lens: Snapchat runs it in a sandbox, from an uplo
|
|
|
5
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
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
7
|
- The zip must stay under 8 MB and `index.html` under 1 MB.
|
|
8
|
+
- Snapchat disables the camera and microphone for Web Lenses. Keep `getUserMedia`, `getDisplayMedia` and `ImageCapture` out of the build entirely.
|
|
9
|
+
- Motion sensors, geolocation, payments, MIDI, USB and WebRTC are off too, so design for pointer input.
|
|
10
|
+
- `localStorage` and IndexedDB start empty every time the Lens opens. To keep data between launches, use the Web Lens API's [storage](https://developers.snap.com/web-lenses/storage), `snapchat.storage`: check `snapchat.isSupported('storage')` after `init()` resolves, write nothing until the first read answers, and chain writes through one promise. Each value can be up to 32 KiB, and the whole store 100 KiB. `src/lens.ts` keeps the best score this way.
|
|
8
11
|
- `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
12
|
- `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
13
|
- Keep the layout portrait and the HUD inside the safe area, using the `--inset-*` properties from `src/lens.css`.
|
|
@@ -18,8 +18,9 @@ This builds the game into `dist/lens/`, checks it against the [Web Lens upload r
|
|
|
18
18
|
- the build refers to one of its own files by an absolute path, such as `/assets/game.js`
|
|
19
19
|
- the build loads anything from another host, such as a script from a CDN or a web font
|
|
20
20
|
- the build uses something the Lens sandbox blocks, such as an `<iframe>` or a web app manifest
|
|
21
|
+
- the build's code asks for the camera, microphone or screen (`getUserMedia`, `getDisplayMedia` or `ImageCapture`), which Snapchat disables for Web Lenses
|
|
21
22
|
|
|
22
|
-
It warns about any other host your scripts mention, since a Lens can't reach the network. To check the build itself, `npm run start` serves it under the same rules, so a request for anything outside the zip fails in the browser console as it would in Snapchat.
|
|
23
|
+
It warns about any other host your scripts mention, since a Lens can't reach the network. It also warns about code that uses something Snapchat [turns off for Web Lenses](https://developers.snap.com/web-lenses/capabilities): motion sensors, geolocation, payments, MIDI, USB and WebRTC, and `localStorage` or IndexedDB, which start empty every time the Lens opens, so keep data between launches with the Web Lens API's [storage](https://developers.snap.com/web-lenses/storage) instead. To check the build itself, `npm run start` serves it under the same rules, so a request for anything outside the zip, or for a device such as the camera, fails in the browser console as it would in Snapchat.
|
|
23
24
|
|
|
24
25
|
### How it fits together
|
|
25
26
|
|
|
@@ -19,6 +19,32 @@ const BLOCKED_TAGS = ['base', 'embed', 'iframe', 'object'];
|
|
|
19
19
|
// error messages link to
|
|
20
20
|
const TEXT_HOSTS = ['www.w3.org', 'github.com', 'react.dev', 'api.playcanvas.com'];
|
|
21
21
|
|
|
22
|
+
// APIs that open the camera, microphone or screen, which Snapchat disables for Web Lenses
|
|
23
|
+
const CAPTURE_APIS =
|
|
24
|
+
/\b(?:mediaDevices|getUserMedia|webkitGetUserMedia|mozGetUserMedia|getDisplayMedia|ImageCapture)\b/g;
|
|
25
|
+
|
|
26
|
+
// other features Snapchat turns off for Web Lenses: code that relies on them gets nothing, so it's
|
|
27
|
+
// worth a look rather than a failure
|
|
28
|
+
const UNAVAILABLE_APIS = [
|
|
29
|
+
{
|
|
30
|
+
pattern:
|
|
31
|
+
/\b(?:deviceorientation|devicemotion|DeviceOrientationEvent|DeviceMotionEvent|Accelerometer|Gyroscope|Magnetometer|LinearAccelerationSensor|GravitySensor|AbsoluteOrientationSensor|RelativeOrientationSensor)\b/g,
|
|
32
|
+
reason: 'Motion sensors are off in a Web Lens, so design for pointer input rather than tilt'
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
pattern: /\b(?:geolocation|PaymentRequest|requestMIDIAccess|navigator\.usb)\b/g,
|
|
36
|
+
reason: 'Geolocation, payments, MIDI and USB are off in a Web Lens'
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
pattern: /\bRTC(?:PeerConnection|DataChannel)\b/g,
|
|
40
|
+
reason: "A Web Lens can't make WebRTC connections"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
pattern: /\b(?:localStorage|indexedDB)\b/g,
|
|
44
|
+
reason: "localStorage and IndexedDB start empty every time the Lens opens, so keep data between launches with the Web Lens API's storage (snapchat.storage)"
|
|
45
|
+
}
|
|
46
|
+
];
|
|
47
|
+
|
|
22
48
|
const buildDir = path.resolve(process.argv[2] ?? 'dist/lens');
|
|
23
49
|
const { name = 'web-lens' } = JSON.parse(fs.readFileSync('package.json', 'utf8'));
|
|
24
50
|
const zipFile = path.join(path.dirname(buildDir), `${name.replace(/^@/, '').replace('/', '-')}.zip`);
|
|
@@ -134,6 +160,32 @@ for (const file of files.filter((f) => /\.m?js$/.test(f))) {
|
|
|
134
160
|
}
|
|
135
161
|
}
|
|
136
162
|
|
|
163
|
+
// where `pattern` first matches in `source`, as line:column, and every API name it matched
|
|
164
|
+
const findUses = (source, pattern) => {
|
|
165
|
+
const uses = Array.from(source.matchAll(pattern));
|
|
166
|
+
if (!uses.length) return null;
|
|
167
|
+
const before = source.slice(0, uses[0].index);
|
|
168
|
+
return {
|
|
169
|
+
at: `${before.split('\n').length}:${uses[0].index - before.lastIndexOf('\n')}`,
|
|
170
|
+
apis: [...new Set(uses.map(([api]) => api))].join(', ')
|
|
171
|
+
};
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
// features a Lens can't use, in scripts or inline in markup
|
|
175
|
+
for (const file of files.filter((f) => /\.(?:html|m?js)$/.test(f))) {
|
|
176
|
+
const source = read(file);
|
|
177
|
+
const capture = findUses(source, CAPTURE_APIS);
|
|
178
|
+
if (capture) {
|
|
179
|
+
errors.push(
|
|
180
|
+
`${file}:${capture.at}: uses ${capture.apis}. Snapchat disables the camera and microphone for Web Lenses, so remove the capture code`
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
for (const { pattern, reason } of UNAVAILABLE_APIS) {
|
|
184
|
+
const use = findUses(source, pattern);
|
|
185
|
+
if (use) warnings.push(`${file}:${use.at}: uses ${use.apis}. ${reason}`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
137
189
|
// a plain zip: deflated entries with forward-slash names and index.html at the root
|
|
138
190
|
const zipEntries = (entries) => {
|
|
139
191
|
const local = [];
|
|
@@ -10,6 +10,7 @@ A PlayCanvas Engine app in TypeScript, built with Vite. The scene is built in co
|
|
|
10
10
|
- `npm run lint` runs ESLint, and `npm run lint:fix` fixes what it can.
|
|
11
11
|
- `npm run fmt` checks formatting, and `npm run fmt:fix` applies Prettier.
|
|
12
12
|
- `npm run build` builds the production site into `dist/`, and `npm run start` serves it.
|
|
13
|
+
- `npm run dev` and `npm run start` open the page in the default browser.
|
|
13
14
|
|
|
14
15
|
## Layout
|
|
15
16
|
|
|
@@ -13,7 +13,7 @@ npm install
|
|
|
13
13
|
npm run dev
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
The app opens in your browser at <http://localhost:5173>. Edit the files under `src/` and save to see the scene update.
|
|
17
17
|
|
|
18
18
|
## Inspector
|
|
19
19
|
|
|
@@ -23,14 +23,14 @@ Open <http://localhost:5173>. Edit the files under `src/` and save to see the sc
|
|
|
23
23
|
|
|
24
24
|
Run these with `npm run <script>`:
|
|
25
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
|
|
26
|
+
| Script | Description |
|
|
27
|
+
| ----------- | -------------------------------------------------- |
|
|
28
|
+
| `dev` | Start the Vite development server and open the app |
|
|
29
|
+
| `build` | Build for production |
|
|
30
|
+
| `start` | Preview the production build and open the app |
|
|
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
|
|
|
@@ -6,20 +6,20 @@
|
|
|
6
6
|
"type": "module",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"build": "vite build",
|
|
9
|
-
"dev": "vite",
|
|
9
|
+
"dev": "vite --open",
|
|
10
10
|
"fmt": "prettier --check .",
|
|
11
11
|
"fmt:fix": "prettier --write .",
|
|
12
12
|
"lint": "eslint .",
|
|
13
13
|
"lint:fix": "eslint . --fix",
|
|
14
|
-
"start": "vite preview",
|
|
14
|
+
"start": "vite preview --open",
|
|
15
15
|
"typecheck": "tsc --noEmit"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"playcanvas": "^2.
|
|
18
|
+
"playcanvas": "^2.24.0"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
|
-
"@playcanvas/eslint-config": "3.0.0
|
|
22
|
-
"@playcanvas/inspector": "^0.
|
|
21
|
+
"@playcanvas/eslint-config": "3.0.0",
|
|
22
|
+
"@playcanvas/inspector": "^0.5.0",
|
|
23
23
|
"eslint": "^9.39.5",
|
|
24
24
|
"prettier": "^3.9.10",
|
|
25
25
|
"typescript": "~6.0.3",
|
|
@@ -11,6 +11,21 @@ const LENS_SANDBOX = [
|
|
|
11
11
|
"form-action 'none'"
|
|
12
12
|
].join('; ');
|
|
13
13
|
|
|
14
|
+
// Approximates its permissions policy, which turns off device features such as the camera and
|
|
15
|
+
// motion sensors
|
|
16
|
+
const LENS_PERMISSIONS = [
|
|
17
|
+
'camera=()',
|
|
18
|
+
'microphone=()',
|
|
19
|
+
'display-capture=()',
|
|
20
|
+
'accelerometer=()',
|
|
21
|
+
'gyroscope=()',
|
|
22
|
+
'magnetometer=()',
|
|
23
|
+
'geolocation=()',
|
|
24
|
+
'payment=()',
|
|
25
|
+
'usb=()',
|
|
26
|
+
'midi=()'
|
|
27
|
+
].join(', ');
|
|
28
|
+
|
|
14
29
|
// https://vite.dev/config/
|
|
15
30
|
export default defineConfig({
|
|
16
31
|
// Snapchat serves a Lens from its own origin, so every URL in the build must be relative
|
|
@@ -25,10 +40,11 @@ export default defineConfig({
|
|
|
25
40
|
chunkSizeWarningLimit: 3000
|
|
26
41
|
},
|
|
27
42
|
preview: {
|
|
28
|
-
// npm run start serves the build under the Lens rules, so
|
|
29
|
-
// the
|
|
43
|
+
// npm run start serves the build under the Lens rules, so loading anything from outside the
|
|
44
|
+
// zip, or using a device such as the camera, fails in the browser as it would in Snapchat
|
|
30
45
|
headers: {
|
|
31
46
|
'Content-Security-Policy': LENS_SANDBOX,
|
|
47
|
+
'Permissions-Policy': LENS_PERMISSIONS,
|
|
32
48
|
'Cross-Origin-Opener-Policy': 'same-origin',
|
|
33
49
|
'Cross-Origin-Embedder-Policy': 'require-corp'
|
|
34
50
|
}
|
|
@@ -10,6 +10,7 @@ A PlayCanvas app in TypeScript and React, built with Vite. The scene is declared
|
|
|
10
10
|
- `npm run lint` runs ESLint, and `npm run lint:fix` fixes what it can.
|
|
11
11
|
- `npm run fmt` checks formatting, and `npm run fmt:fix` applies Prettier.
|
|
12
12
|
- `npm run build` builds the production site into `dist/`, and `npm run start` serves it.
|
|
13
|
+
- `npm run dev` and `npm run start` open the page in the default browser.
|
|
13
14
|
|
|
14
15
|
## Layout
|
|
15
16
|
|
|
@@ -13,7 +13,7 @@ npm install
|
|
|
13
13
|
npm run dev
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
The app opens in your browser at <http://localhost:5173>. Edit the files under `src/` and save to see the scene update.
|
|
17
17
|
|
|
18
18
|
## Inspector
|
|
19
19
|
|
|
@@ -23,14 +23,14 @@ Open <http://localhost:5173>. Edit the files under `src/` and save to see the sc
|
|
|
23
23
|
|
|
24
24
|
Run these with `npm run <script>`:
|
|
25
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
|
|
26
|
+
| Script | Description |
|
|
27
|
+
| ----------- | -------------------------------------------------- |
|
|
28
|
+
| `dev` | Start the Vite development server and open the app |
|
|
29
|
+
| `build` | Build for production |
|
|
30
|
+
| `start` | Preview the production build and open the app |
|
|
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
|
|
|
@@ -1,15 +1,4 @@
|
|
|
1
1
|
import reactConfig from '@playcanvas/eslint-config/react';
|
|
2
2
|
import typescriptConfig from '@playcanvas/eslint-config/typescript';
|
|
3
3
|
|
|
4
|
-
export default [
|
|
5
|
-
{ ignores: ['.claude', '.agents', 'public/ammo'] },
|
|
6
|
-
...typescriptConfig,
|
|
7
|
-
...reactConfig,
|
|
8
|
-
{
|
|
9
|
-
rules: {
|
|
10
|
-
'react/jsx-uses-react': 'off',
|
|
11
|
-
'react/prop-types': 'off',
|
|
12
|
-
'react/react-in-jsx-scope': 'off'
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
];
|
|
4
|
+
export default [{ ignores: ['.claude', '.agents', 'public/ammo'] }, ...typescriptConfig, ...reactConfig];
|
|
@@ -6,26 +6,26 @@
|
|
|
6
6
|
"type": "module",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"build": "vite build",
|
|
9
|
-
"dev": "vite",
|
|
9
|
+
"dev": "vite --open",
|
|
10
10
|
"fmt": "prettier --check .",
|
|
11
11
|
"fmt:fix": "prettier --write .",
|
|
12
12
|
"lint": "eslint .",
|
|
13
13
|
"lint:fix": "eslint . --fix",
|
|
14
|
-
"start": "vite preview",
|
|
14
|
+
"start": "vite preview --open",
|
|
15
15
|
"typecheck": "tsc --noEmit"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"@playcanvas/react": "^0.11.7",
|
|
19
|
-
"playcanvas": "^2.
|
|
19
|
+
"playcanvas": "^2.24.0",
|
|
20
20
|
"react": "^19.3.1",
|
|
21
21
|
"react-dom": "^19.3.1"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@playcanvas/eslint-config": "3.0.0
|
|
25
|
-
"@playcanvas/inspector": "^0.
|
|
24
|
+
"@playcanvas/eslint-config": "3.0.0",
|
|
25
|
+
"@playcanvas/inspector": "^0.5.0",
|
|
26
26
|
"@types/react": "^19.3.0",
|
|
27
27
|
"@types/react-dom": "^19.3.0",
|
|
28
|
-
"@vitejs/plugin-react": "^6.1
|
|
28
|
+
"@vitejs/plugin-react": "^6.2.1",
|
|
29
29
|
"eslint": "^9.39.5",
|
|
30
30
|
"prettier": "^3.9.10",
|
|
31
31
|
"typescript": "~6.0.3",
|
|
@@ -12,6 +12,21 @@ const LENS_SANDBOX = [
|
|
|
12
12
|
"form-action 'none'"
|
|
13
13
|
].join('; ');
|
|
14
14
|
|
|
15
|
+
// Approximates its permissions policy, which turns off device features such as the camera and
|
|
16
|
+
// motion sensors
|
|
17
|
+
const LENS_PERMISSIONS = [
|
|
18
|
+
'camera=()',
|
|
19
|
+
'microphone=()',
|
|
20
|
+
'display-capture=()',
|
|
21
|
+
'accelerometer=()',
|
|
22
|
+
'gyroscope=()',
|
|
23
|
+
'magnetometer=()',
|
|
24
|
+
'geolocation=()',
|
|
25
|
+
'payment=()',
|
|
26
|
+
'usb=()',
|
|
27
|
+
'midi=()'
|
|
28
|
+
].join(', ');
|
|
29
|
+
|
|
15
30
|
// https://vite.dev/config/
|
|
16
31
|
export default defineConfig({
|
|
17
32
|
// Snapchat serves a Lens from its own origin, so every URL in the build must be relative
|
|
@@ -26,10 +41,11 @@ export default defineConfig({
|
|
|
26
41
|
chunkSizeWarningLimit: 3000
|
|
27
42
|
},
|
|
28
43
|
preview: {
|
|
29
|
-
// npm run start serves the build under the Lens rules, so
|
|
30
|
-
// the
|
|
44
|
+
// npm run start serves the build under the Lens rules, so loading anything from outside the
|
|
45
|
+
// zip, or using a device such as the camera, fails in the browser as it would in Snapchat
|
|
31
46
|
headers: {
|
|
32
47
|
'Content-Security-Policy': LENS_SANDBOX,
|
|
48
|
+
'Permissions-Policy': LENS_PERMISSIONS,
|
|
33
49
|
'Cross-Origin-Opener-Policy': 'same-origin',
|
|
34
50
|
'Cross-Origin-Embedder-Policy': 'require-corp'
|
|
35
51
|
}
|
|
@@ -10,6 +10,7 @@ A PlayCanvas app in TypeScript, built with Vite. The scene is declared in HTML w
|
|
|
10
10
|
- `npm run lint` runs ESLint, and `npm run lint:fix` fixes what it can.
|
|
11
11
|
- `npm run fmt` checks formatting, and `npm run fmt:fix` applies Prettier.
|
|
12
12
|
- `npm run build` builds the production site into `dist/`, and `npm run start` serves it.
|
|
13
|
+
- `npm run dev` and `npm run start` open the page in the default browser.
|
|
13
14
|
|
|
14
15
|
## Layout
|
|
15
16
|
|
|
@@ -14,7 +14,7 @@ npm install
|
|
|
14
14
|
npm run dev
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
The app opens in your browser at <http://localhost:5173>. Edit `index.html` to change the scene.
|
|
18
18
|
|
|
19
19
|
## Inspector
|
|
20
20
|
|
|
@@ -24,14 +24,14 @@ Open <http://localhost:5173>. Edit `index.html` to change the scene.
|
|
|
24
24
|
|
|
25
25
|
Run these with `npm run <script>`:
|
|
26
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
|
|
27
|
+
| Script | Description |
|
|
28
|
+
| ----------- | -------------------------------------------------- |
|
|
29
|
+
| `dev` | Start the Vite development server and open the app |
|
|
30
|
+
| `build` | Build for production |
|
|
31
|
+
| `start` | Preview the production build and open the app |
|
|
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
|
|
|
@@ -6,21 +6,21 @@
|
|
|
6
6
|
"type": "module",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"build": "vite build",
|
|
9
|
-
"dev": "vite",
|
|
9
|
+
"dev": "vite --open",
|
|
10
10
|
"fmt": "prettier --check .",
|
|
11
11
|
"fmt:fix": "prettier --write .",
|
|
12
12
|
"lint": "eslint .",
|
|
13
13
|
"lint:fix": "eslint . --fix",
|
|
14
|
-
"start": "vite preview",
|
|
14
|
+
"start": "vite preview --open",
|
|
15
15
|
"typecheck": "tsc --noEmit"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@playcanvas/web-components": "^0.
|
|
19
|
-
"playcanvas": "^2.
|
|
18
|
+
"@playcanvas/web-components": "^0.29.0",
|
|
19
|
+
"playcanvas": "^2.24.0"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
|
-
"@playcanvas/eslint-config": "3.0.0
|
|
23
|
-
"@playcanvas/inspector": "^0.
|
|
22
|
+
"@playcanvas/eslint-config": "3.0.0",
|
|
23
|
+
"@playcanvas/inspector": "^0.5.0",
|
|
24
24
|
"eslint": "^9.39.5",
|
|
25
25
|
"prettier": "^3.9.10",
|
|
26
26
|
"typescript": "~6.0.3",
|
|
@@ -11,6 +11,21 @@ const LENS_SANDBOX = [
|
|
|
11
11
|
"form-action 'none'"
|
|
12
12
|
].join('; ');
|
|
13
13
|
|
|
14
|
+
// Approximates its permissions policy, which turns off device features such as the camera and
|
|
15
|
+
// motion sensors
|
|
16
|
+
const LENS_PERMISSIONS = [
|
|
17
|
+
'camera=()',
|
|
18
|
+
'microphone=()',
|
|
19
|
+
'display-capture=()',
|
|
20
|
+
'accelerometer=()',
|
|
21
|
+
'gyroscope=()',
|
|
22
|
+
'magnetometer=()',
|
|
23
|
+
'geolocation=()',
|
|
24
|
+
'payment=()',
|
|
25
|
+
'usb=()',
|
|
26
|
+
'midi=()'
|
|
27
|
+
].join(', ');
|
|
28
|
+
|
|
14
29
|
// https://vite.dev/config/
|
|
15
30
|
export default defineConfig({
|
|
16
31
|
// Snapchat serves a Lens from its own origin, so every URL in the build must be relative
|
|
@@ -25,10 +40,11 @@ export default defineConfig({
|
|
|
25
40
|
chunkSizeWarningLimit: 3000
|
|
26
41
|
},
|
|
27
42
|
preview: {
|
|
28
|
-
// npm run start serves the build under the Lens rules, so
|
|
29
|
-
// the
|
|
43
|
+
// npm run start serves the build under the Lens rules, so loading anything from outside the
|
|
44
|
+
// zip, or using a device such as the camera, fails in the browser as it would in Snapchat
|
|
30
45
|
headers: {
|
|
31
46
|
'Content-Security-Policy': LENS_SANDBOX,
|
|
47
|
+
'Permissions-Policy': LENS_PERMISSIONS,
|
|
32
48
|
'Cross-Origin-Opener-Policy': 'same-origin',
|
|
33
49
|
'Cross-Origin-Embedder-Policy': 'require-corp'
|
|
34
50
|
}
|