@vgai/engine 0.5.42 → 0.5.45
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/dist/adapter/adapter-module.d.ts +28 -29
- package/dist/adapter/adapter-module.d.ts.map +1 -1
- package/dist/adapter/adapter-module.js +11 -34
- package/dist/adapter/authoring-seam-contract.d.ts +5 -0
- package/dist/adapter/authoring-seam-contract.d.ts.map +1 -1
- package/dist/adapter/authoring-seam-contract.js +1 -0
- package/dist/adapter/authoring.d.ts +16 -0
- package/dist/adapter/authoring.d.ts.map +1 -1
- package/dist/adapter/finders/finder-result.d.ts +2 -2
- package/dist/adapter/finders/finder-result.d.ts.map +1 -1
- package/dist/adapter/finders/index.d.ts +15 -2
- package/dist/adapter/finders/index.d.ts.map +1 -1
- package/dist/adapter/finders/index.js +38 -10
- package/dist/adapter/finders/prefabs-from-stories.d.ts.map +1 -1
- package/dist/adapter/finders/registry.d.ts +45 -0
- package/dist/adapter/finders/registry.d.ts.map +1 -0
- package/dist/adapter/finders/registry.js +56 -0
- package/dist/adapter/ingest/scene-capture.d.ts.map +1 -1
- package/dist/adapter/ingest/scene-capture.js +50 -20
- package/dist/adapter/system-adapter.d.ts +3 -1
- package/dist/adapter/system-adapter.d.ts.map +1 -1
- package/dist/canvas-react/pixi-react-root-factory.d.ts.map +1 -1
- package/dist/canvas-react/pixi-react-root-factory.js +21 -11
- package/dist/data/vite-plugin-data.d.ts +1 -1
- package/dist/data/vite-plugin-data.d.ts.map +1 -1
- package/dist/data/vite-plugin-data.js +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/input/host-pointer.d.ts +18 -0
- package/dist/input/host-pointer.d.ts.map +1 -1
- package/dist/input/host-pointer.js +54 -5
- package/dist/input/input-manager.d.ts +83 -1
- package/dist/input/input-manager.d.ts.map +1 -1
- package/dist/input/input-manager.js +357 -8
- package/dist/manifest/configuration-kinds.d.ts +97 -0
- package/dist/manifest/configuration-kinds.d.ts.map +1 -0
- package/dist/manifest/configuration-kinds.js +148 -0
- package/dist/manifest/index.d.ts +2 -1
- package/dist/manifest/index.d.ts.map +1 -1
- package/dist/manifest/index.js +4 -3
- package/dist/manifest/kind-modules.d.ts +18 -0
- package/dist/manifest/kind-modules.d.ts.map +1 -0
- package/dist/manifest/kind-modules.js +58 -0
- package/dist/manifest/load-file.d.ts +3 -3
- package/dist/manifest/load-file.d.ts.map +1 -1
- package/dist/manifest/load-file.js +4 -4
- package/dist/manifest/load.d.ts +17 -12
- package/dist/manifest/load.d.ts.map +1 -1
- package/dist/manifest/load.js +29 -19
- package/dist/manifest/runtime-environment.d.ts +11 -0
- package/dist/manifest/runtime-environment.d.ts.map +1 -0
- package/dist/manifest/runtime-environment.js +44 -0
- package/dist/manifest/schema.d.ts +10 -9
- package/dist/manifest/schema.d.ts.map +1 -1
- package/dist/manifest/schema.js +32 -27
- package/dist/react/use-data.d.ts +1 -1
- package/dist/react/use-data.js +1 -1
- package/dist/render/matcap-texture.d.ts +5 -0
- package/dist/render/matcap-texture.d.ts.map +1 -0
- package/dist/render/matcap-texture.js +82 -0
- package/dist/render/soft-particle-depth.d.ts +14 -8
- package/dist/render/soft-particle-depth.d.ts.map +1 -1
- package/dist/render/soft-particle-depth.js +37 -14
- package/dist/render/viewport-shading.d.ts +11 -1
- package/dist/render/viewport-shading.d.ts.map +1 -1
- package/dist/render/viewport-shading.js +25 -0
- package/dist/runtime/create-runtime.js +6 -5
- package/dist/runtime/mount-manifest.d.ts.map +1 -1
- package/dist/runtime/mount-manifest.js +11 -2
- package/dist/settings/schema.d.ts +99 -0
- package/dist/settings/schema.d.ts.map +1 -0
- package/dist/settings/schema.js +110 -0
- package/dist/world3d-react/r3f-root-factory.d.ts.map +1 -1
- package/dist/world3d-react/r3f-root-factory.js +34 -36
- package/dist/world3d-react/rapier-physics-bridge.d.ts.map +1 -1
- package/dist/world3d-react/rapier-physics-bridge.js +21 -6
- package/dist-config/config.js +1 -1
- package/dist-config/config.js.map +2 -2
- package/package.json +2 -1
- package/schemas/engine-capabilities.json +4 -4
- package/schemas/vgai-project.schema.json +41 -36
- package/schemas/vgai-settings.schema.json +112 -0
- package/src/adapter/adapter-module.ts +33 -51
- package/src/adapter/authoring-seam-contract.ts +1 -0
- package/src/adapter/authoring.ts +16 -0
- package/src/adapter/finders/finder-result.ts +2 -2
- package/src/adapter/finders/index.ts +68 -12
- package/src/adapter/finders/prefabs-from-stories.ts +2 -2
- package/src/adapter/finders/registry.ts +95 -0
- package/src/adapter/finders/scenes-from-entrypoint-selection.ts +4 -4
- package/src/adapter/ingest/scene-capture.ts +48 -19
- package/src/adapter/system-adapter.ts +3 -1
- package/src/canvas-react/pixi-react-root-factory.tsx +21 -11
- package/src/data/vite-plugin-data.ts +4 -2
- package/src/index.ts +1 -1
- package/src/input/host-pointer.ts +84 -5
- package/src/input/input-manager.ts +404 -7
- package/src/manifest/configuration-kinds.ts +241 -0
- package/src/manifest/index.ts +4 -3
- package/src/manifest/kind-modules.ts +58 -0
- package/src/manifest/load-file.ts +11 -5
- package/src/manifest/load.ts +53 -22
- package/src/manifest/runtime-environment.ts +48 -0
- package/src/manifest/schema.ts +39 -33
- package/src/react/use-data.ts +1 -1
- package/src/render/matcap-texture.ts +92 -0
- package/src/render/soft-particle-depth.ts +43 -14
- package/src/render/viewport-shading.ts +33 -1
- package/src/runtime/create-runtime.ts +6 -5
- package/src/runtime/mount-manifest.ts +13 -2
- package/src/settings/schema.ts +127 -0
- package/src/world3d-react/r3f-root-factory.tsx +45 -39
- package/src/world3d-react/rapier-physics-bridge.tsx +22 -8
package/dist/react/use-data.d.ts
CHANGED
|
@@ -48,7 +48,7 @@ export declare function useData<T>(handle: DataHandle<T>): T;
|
|
|
48
48
|
* runtime state belongs in your game's own store, never written back into
|
|
49
49
|
* data files. Outside a running editor there is no `/__editor` server, so
|
|
50
50
|
* this rejects with a teaching error (a shipped game could never reach the
|
|
51
|
-
* route anyway — builds strip `src/tools/` entirely, §4).
|
|
51
|
+
* route anyway — builds strip `src/contributions/` and `src/tools/` entirely, §4).
|
|
52
52
|
*
|
|
53
53
|
* The body is PARSED VALUES, never serialized text: the dev server's fold
|
|
54
54
|
* (`packages/editor/server/data-file-serialize.ts`) owns key order and the
|
package/dist/react/use-data.js
CHANGED
|
@@ -50,7 +50,7 @@ export function useData(handle) {
|
|
|
50
50
|
* runtime state belongs in your game's own store, never written back into
|
|
51
51
|
* data files. Outside a running editor there is no `/__editor` server, so
|
|
52
52
|
* this rejects with a teaching error (a shipped game could never reach the
|
|
53
|
-
* route anyway — builds strip `src/tools/` entirely, §4).
|
|
53
|
+
* route anyway — builds strip `src/contributions/` and `src/tools/` entirely, §4).
|
|
54
54
|
*
|
|
55
55
|
* The body is PARSED VALUES, never serialized text: the dev server's fold
|
|
56
56
|
* (`packages/editor/server/data-file-serialize.ts`) owns key order and the
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
export declare function drawNeutralMatcap(size?: number): HTMLCanvasElement;
|
|
3
|
+
/** The drawn sphere as a ready-to-sample texture. Callers own disposal. */
|
|
4
|
+
export declare function createNeutralMatcapTexture(size?: number): THREE.CanvasTexture;
|
|
5
|
+
//# sourceMappingURL=matcap-texture.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"matcap-texture.d.ts","sourceRoot":"","sources":["../../src/render/matcap-texture.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AA8B/B,wBAAgB,iBAAiB,CAAC,IAAI,SAAO,GAAG,iBAAiB,CAkDhE;AAED,2EAA2E;AAC3E,wBAAgB,0BAA0B,CAAC,IAAI,SAAO,GAAG,KAAK,CAAC,aAAa,CAQ3E"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
/**
|
|
3
|
+
* The neutral matcap sphere, DRAWN rather than shipped.
|
|
4
|
+
*
|
|
5
|
+
* A matcap is just a picture of a lit sphere sampled by view-space normal, so
|
|
6
|
+
* there is nothing to vendor: the same three numbers a shader would use
|
|
7
|
+
* (key, fill, rim) evaluated once per texel produce the image. Keeping it
|
|
8
|
+
* procedural means the diagnostic look has no binary asset, no license, no
|
|
9
|
+
* provenance row, and no way to go missing from a build.
|
|
10
|
+
*
|
|
11
|
+
* The look is deliberately CLAY: one warm key from the upper left, a cool
|
|
12
|
+
* fill from the lower right, a tight rim, and a broad soft highlight — the
|
|
13
|
+
* neutral sculpting material whose whole job is to let form read without
|
|
14
|
+
* colour, texture or authored lighting getting a vote.
|
|
15
|
+
*/
|
|
16
|
+
const SIZE = 256;
|
|
17
|
+
/** sRGB transfer curve — the canvas holds display-referred bytes. */
|
|
18
|
+
function encode(value) {
|
|
19
|
+
const c = Math.min(1, Math.max(0, value));
|
|
20
|
+
return c <= 0.0031308 ? c * 12.92 : 1.055 * c ** (1 / 2.4) - 0.055;
|
|
21
|
+
}
|
|
22
|
+
function normalize(x, y, z) {
|
|
23
|
+
const length = Math.hypot(x, y, z) || 1;
|
|
24
|
+
return [x / length, y / length, z / length];
|
|
25
|
+
}
|
|
26
|
+
export function drawNeutralMatcap(size = SIZE) {
|
|
27
|
+
const canvas = document.createElement('canvas');
|
|
28
|
+
canvas.width = size;
|
|
29
|
+
canvas.height = size;
|
|
30
|
+
const context = canvas.getContext('2d');
|
|
31
|
+
if (!context)
|
|
32
|
+
return canvas;
|
|
33
|
+
const image = context.createImageData(size, size);
|
|
34
|
+
const key = normalize(-0.45, 0.62, 0.64);
|
|
35
|
+
const fill = normalize(0.65, -0.35, 0.5);
|
|
36
|
+
// View direction is +Z for a matcap: the sphere is drawn facing the camera.
|
|
37
|
+
const half = normalize(key[0], key[1], key[2] + 1);
|
|
38
|
+
const base = [0.7, 0.69, 0.68];
|
|
39
|
+
const fillColor = [0.34, 0.4, 0.52];
|
|
40
|
+
const rimColor = [0.85, 0.88, 1];
|
|
41
|
+
for (let py = 0; py < size; py++) {
|
|
42
|
+
for (let px = 0; px < size; px++) {
|
|
43
|
+
const nx = (px + 0.5) / size / 0.5 - 1;
|
|
44
|
+
const ny = 1 - (py + 0.5) / size / 0.5;
|
|
45
|
+
const r2 = nx * nx + ny * ny;
|
|
46
|
+
// Outside the unit disc a matcap is never sampled by a facing surface,
|
|
47
|
+
// but bilinear filtering reaches one texel past the silhouette. Clamp
|
|
48
|
+
// to the rim normal so the edge stays the rim colour instead of
|
|
49
|
+
// bleeding whatever happened to be there.
|
|
50
|
+
const clamped = r2 > 1;
|
|
51
|
+
const scale = clamped ? 1 / Math.sqrt(r2) : 1;
|
|
52
|
+
const x = nx * scale;
|
|
53
|
+
const y = ny * scale;
|
|
54
|
+
const z = clamped ? 0 : Math.sqrt(Math.max(0, 1 - r2));
|
|
55
|
+
const diffuse = Math.max(0, x * key[0] + y * key[1] + z * key[2]);
|
|
56
|
+
const fillTerm = Math.max(0, x * fill[0] + y * fill[1] + z * fill[2]);
|
|
57
|
+
const specular = Math.max(0, x * half[0] + y * half[1] + z * half[2]) ** 42 * 0.5;
|
|
58
|
+
const rim = (1 - z) ** 3.2 * 0.4;
|
|
59
|
+
const offset = (py * size + px) * 4;
|
|
60
|
+
for (let channel = 0; channel < 3; channel++) {
|
|
61
|
+
const lit = base[channel] * (0.16 + 0.8 * diffuse) +
|
|
62
|
+
fillColor[channel] * 0.3 * fillTerm +
|
|
63
|
+
rimColor[channel] * rim +
|
|
64
|
+
specular;
|
|
65
|
+
image.data[offset + channel] = Math.round(encode(lit) * 255);
|
|
66
|
+
}
|
|
67
|
+
image.data[offset + 3] = 255;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
context.putImageData(image, 0, 0);
|
|
71
|
+
return canvas;
|
|
72
|
+
}
|
|
73
|
+
/** The drawn sphere as a ready-to-sample texture. Callers own disposal. */
|
|
74
|
+
export function createNeutralMatcapTexture(size = SIZE) {
|
|
75
|
+
const texture = new THREE.CanvasTexture(drawNeutralMatcap(size));
|
|
76
|
+
texture.colorSpace = THREE.SRGBColorSpace;
|
|
77
|
+
texture.minFilter = THREE.LinearFilter;
|
|
78
|
+
texture.magFilter = THREE.LinearFilter;
|
|
79
|
+
texture.generateMipmaps = false;
|
|
80
|
+
texture.needsUpdate = true;
|
|
81
|
+
return texture;
|
|
82
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* `
|
|
2
|
+
* Opaque scene DEPTH for native consumers, including three.quarks soft particles and translated
|
|
3
|
+
* Godot `hint_depth_texture` shader uniforms.
|
|
4
4
|
*
|
|
5
5
|
* ## What three.quarks needs, and what it does when nobody supplies it
|
|
6
6
|
*
|
|
@@ -61,12 +61,10 @@
|
|
|
61
61
|
* with the armed particle renderers and every transparent surface hidden and a
|
|
62
62
|
* colour-write-disabled override material in place.
|
|
63
63
|
*
|
|
64
|
-
* That cost is paid ONLY while something is armed.
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
* world with no soft particles pays one `Set.size` read per frame and nothing
|
|
69
|
-
* else. Armed, per frame per camera it costs: one scene traverse (to find the
|
|
64
|
+
* That cost is paid ONLY while something is armed. Soft particles enter through
|
|
65
|
+
* {@link armSoftParticleDepth}; translated depth-texture shader programs enter through
|
|
66
|
+
* {@link armSceneDepthTexture}. With both registries empty {@link SoftParticleDepthPass.render}
|
|
67
|
+
* returns before it touches the scene. Armed, per frame per camera it costs: one scene traverse (to find the
|
|
70
68
|
* transparent surfaces to hide), one extra geometry-only scene draw with shadow
|
|
71
69
|
* updates suppressed, and a drawing-buffer-sized depth target
|
|
72
70
|
* (`UnsignedIntType` depth + an unread RGBA8 colour attachment ≈ 8
|
|
@@ -97,6 +95,10 @@
|
|
|
97
95
|
*/
|
|
98
96
|
import * as THREE from 'three';
|
|
99
97
|
import type { BatchedRenderer } from 'three.quarks';
|
|
98
|
+
/** Any native material/program that samples Godot's opaque-pass depth texture. */
|
|
99
|
+
export interface SceneDepthTextureConsumer {
|
|
100
|
+
setDepthTexture(texture: THREE.Texture): void;
|
|
101
|
+
}
|
|
100
102
|
/**
|
|
101
103
|
* A 1x1 texture whose red channel is 1.0 — "the depth buffer says nothing is in
|
|
102
104
|
* front of anything".
|
|
@@ -120,6 +122,10 @@ export declare function armSoftParticleDepth(renderer: BatchedRenderer): void;
|
|
|
120
122
|
* depth nobody is refreshing.
|
|
121
123
|
*/
|
|
122
124
|
export declare function disarmSoftParticleDepth(renderer: BatchedRenderer): void;
|
|
125
|
+
/** Retain one consumer of the same opaque scene-depth image Godot exposes as hint_depth_texture. */
|
|
126
|
+
export declare function armSceneDepthTexture(consumer: SceneDepthTextureConsumer): void;
|
|
127
|
+
/** Release one retained Godot depth-texture consumer without leaving a disposed target bound. */
|
|
128
|
+
export declare function disarmSceneDepthTexture(consumer: SceneDepthTextureConsumer): void;
|
|
123
129
|
/** Test/diagnostic read of the registry — how many renderers a pass would consider. */
|
|
124
130
|
export declare function armedSoftParticleRendererCount(): number;
|
|
125
131
|
/** The subset of `THREE.WebGLRenderer` this pass drives, so a host can supply its own. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"soft-particle-depth.d.ts","sourceRoot":"","sources":["../../src/render/soft-particle-depth.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"soft-particle-depth.d.ts","sourceRoot":"","sources":["../../src/render/soft-particle-depth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8FG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAapD,kFAAkF;AAClF,MAAM,WAAW,yBAAyB;IACxC,eAAe,CAAC,OAAO,EAAE,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC;CAC/C;AAMD;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,IAAI,KAAK,CAAC,WAAW,CAOnD;AAED,8FAA8F;AAC9F,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI,CAEpE;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI,CAQvE;AAED,oGAAoG;AACpG,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,yBAAyB,GAAG,IAAI,CAG9E;AAED,iGAAiG;AACjG,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,yBAAyB,GAAG,IAAI,CAQjF;AAED,uFAAuF;AACvF,wBAAgB,8BAA8B,IAAI,MAAM,CAEvD;AAED,0FAA0F;AAC1F,MAAM,WAAW,yBAAyB;IACxC,oBAAoB,CAAC,MAAM,EAAE,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;IAC3D,eAAe,IAAI,KAAK,CAAC,iBAAiB,GAAG,IAAI,CAAC;IAClD,eAAe,CAAC,MAAM,EAAE,KAAK,CAAC,iBAAiB,GAAG,IAAI,GAAG,IAAI,CAAC;IAC9D,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC;IAC1D,kGAAkG;IAClG,QAAQ,CAAC,SAAS,CAAC,EAAE;QAAE,UAAU,EAAE,OAAO,CAAC;QAAC,WAAW,EAAE,OAAO,CAAA;KAAE,CAAC;CACpE;AAED,MAAM,WAAW,qBAAqB;IACpC;;;;;;OAMG;IACH,MAAM,CAAC,QAAQ,EAAE,yBAAyB,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC;IAC5F,OAAO,IAAI,IAAI,CAAC;CACjB;AA0CD,wBAAgB,2BAA2B,IAAI,qBAAqB,CAiHnE"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* `
|
|
2
|
+
* Opaque scene DEPTH for native consumers, including three.quarks soft particles and translated
|
|
3
|
+
* Godot `hint_depth_texture` shader uniforms.
|
|
4
4
|
*
|
|
5
5
|
* ## What three.quarks needs, and what it does when nobody supplies it
|
|
6
6
|
*
|
|
@@ -61,12 +61,10 @@
|
|
|
61
61
|
* with the armed particle renderers and every transparent surface hidden and a
|
|
62
62
|
* colour-write-disabled override material in place.
|
|
63
63
|
*
|
|
64
|
-
* That cost is paid ONLY while something is armed.
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
* world with no soft particles pays one `Set.size` read per frame and nothing
|
|
69
|
-
* else. Armed, per frame per camera it costs: one scene traverse (to find the
|
|
64
|
+
* That cost is paid ONLY while something is armed. Soft particles enter through
|
|
65
|
+
* {@link armSoftParticleDepth}; translated depth-texture shader programs enter through
|
|
66
|
+
* {@link armSceneDepthTexture}. With both registries empty {@link SoftParticleDepthPass.render}
|
|
67
|
+
* returns before it touches the scene. Armed, per frame per camera it costs: one scene traverse (to find the
|
|
70
68
|
* transparent surfaces to hide), one extra geometry-only scene draw with shadow
|
|
71
69
|
* updates suppressed, and a drawing-buffer-sized depth target
|
|
72
70
|
* (`UnsignedIntType` depth + an unread RGBA8 colour attachment ≈ 8
|
|
@@ -106,6 +104,7 @@ import * as THREE from 'three';
|
|
|
106
104
|
* renderers actually parented under the scene it is rendering.
|
|
107
105
|
*/
|
|
108
106
|
const armed = new Map();
|
|
107
|
+
const depthTextureConsumers = new Map();
|
|
109
108
|
let farDepth;
|
|
110
109
|
/**
|
|
111
110
|
* A 1x1 texture whose red channel is 1.0 — "the depth buffer says nothing is in
|
|
@@ -148,6 +147,22 @@ export function disarmSoftParticleDepth(renderer) {
|
|
|
148
147
|
return;
|
|
149
148
|
renderer.setDepthTexture(farDepthTexture());
|
|
150
149
|
}
|
|
150
|
+
/** Retain one consumer of the same opaque scene-depth image Godot exposes as hint_depth_texture. */
|
|
151
|
+
export function armSceneDepthTexture(consumer) {
|
|
152
|
+
depthTextureConsumers.set(consumer, (depthTextureConsumers.get(consumer) ?? 0) + 1);
|
|
153
|
+
consumer.setDepthTexture(farDepthTexture());
|
|
154
|
+
}
|
|
155
|
+
/** Release one retained Godot depth-texture consumer without leaving a disposed target bound. */
|
|
156
|
+
export function disarmSceneDepthTexture(consumer) {
|
|
157
|
+
const remaining = (depthTextureConsumers.get(consumer) ?? 0) - 1;
|
|
158
|
+
if (remaining > 0) {
|
|
159
|
+
depthTextureConsumers.set(consumer, remaining);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
if (!depthTextureConsumers.delete(consumer))
|
|
163
|
+
return;
|
|
164
|
+
consumer.setDepthTexture(farDepthTexture());
|
|
165
|
+
}
|
|
151
166
|
/** Test/diagnostic read of the registry — how many renderers a pass would consider. */
|
|
152
167
|
export function armedSoftParticleRendererCount() {
|
|
153
168
|
return armed.size;
|
|
@@ -210,14 +225,14 @@ export function createSoftParticleDepthPass() {
|
|
|
210
225
|
};
|
|
211
226
|
return {
|
|
212
227
|
render(renderer, scene, camera) {
|
|
213
|
-
if (armed.size === 0)
|
|
228
|
+
if (armed.size === 0 && depthTextureConsumers.size === 0)
|
|
214
229
|
return;
|
|
215
230
|
const consumers = [];
|
|
216
231
|
for (const candidate of armed.keys()) {
|
|
217
232
|
if (isUnder(candidate, scene))
|
|
218
233
|
consumers.push(candidate);
|
|
219
234
|
}
|
|
220
|
-
if (consumers.length === 0)
|
|
235
|
+
if (consumers.length === 0 && depthTextureConsumers.size === 0)
|
|
221
236
|
return;
|
|
222
237
|
// See REQUIRED_RENDERER_MEMBERS: a renderer that cannot swap render
|
|
223
238
|
// targets cannot run a prepass, and the frame is worth more than the
|
|
@@ -227,9 +242,9 @@ export function createSoftParticleDepthPass() {
|
|
|
227
242
|
if (!warnedIncapable) {
|
|
228
243
|
warnedIncapable = true;
|
|
229
244
|
// biome-ignore lint/suspicious/noConsole: deliberate loud degrade — the documented alternative to throwing out of the frame
|
|
230
|
-
console.warn(`
|
|
231
|
-
`${String(consumers.length)} particle batch(es)
|
|
232
|
-
'
|
|
245
|
+
console.warn(`opaque scene-depth prepass skipped: this renderer is missing ${missing.join(', ')}. ` +
|
|
246
|
+
`${String(consumers.length)} particle batch(es) and ${String(depthTextureConsumers.size)} ` +
|
|
247
|
+
'shader consumer(s) keep the inert far-depth default. A real ' +
|
|
233
248
|
'WebGLRenderer has these; a design-time settle stand-in does not.');
|
|
234
249
|
}
|
|
235
250
|
return;
|
|
@@ -284,14 +299,22 @@ export function createSoftParticleDepthPass() {
|
|
|
284
299
|
object.visible = true;
|
|
285
300
|
hidden.length = 0;
|
|
286
301
|
}
|
|
302
|
+
const renderedDepth = rt.depthTexture;
|
|
303
|
+
if (renderedDepth === null) {
|
|
304
|
+
throw new Error('opaque scene-depth prepass lost its authored depth texture.');
|
|
305
|
+
}
|
|
287
306
|
for (const consumer of consumers)
|
|
288
|
-
consumer.setDepthTexture(
|
|
307
|
+
consumer.setDepthTexture(renderedDepth);
|
|
308
|
+
for (const consumer of depthTextureConsumers.keys())
|
|
309
|
+
consumer.setDepthTexture(renderedDepth);
|
|
289
310
|
},
|
|
290
311
|
dispose() {
|
|
291
312
|
// Every consumer this pass fed now points at a texture that is about to
|
|
292
313
|
// go away. Hand them the inert default back rather than a disposed one.
|
|
293
314
|
for (const candidate of armed.keys())
|
|
294
315
|
candidate.setDepthTexture(farDepthTexture());
|
|
316
|
+
for (const consumer of depthTextureConsumers.keys())
|
|
317
|
+
consumer.setDepthTexture(farDepthTexture());
|
|
295
318
|
target?.dispose();
|
|
296
319
|
target = null;
|
|
297
320
|
override.dispose();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as THREE from 'three';
|
|
2
2
|
/** Temporary developer-facing shading modes. These never become scene data. */
|
|
3
|
-
export type ViewportShadingMode = 'solid' | 'clay' | 'unlit' | 'wireframe' | 'normals' | 'overdraw';
|
|
3
|
+
export type ViewportShadingMode = 'solid' | 'clay' | 'unlit' | 'wireframe' | 'matcap' | 'normals' | 'overdraw';
|
|
4
4
|
/**
|
|
5
5
|
* Applies a render-only material view and restores every native material in a
|
|
6
6
|
* `finally` block. Gameplay and authoring therefore always observe the real
|
|
@@ -9,6 +9,16 @@ export type ViewportShadingMode = 'solid' | 'clay' | 'unlit' | 'wireframe' | 'no
|
|
|
9
9
|
export declare class ViewportShadingRenderer {
|
|
10
10
|
private readonly _derived;
|
|
11
11
|
private readonly _normals;
|
|
12
|
+
/**
|
|
13
|
+
* The clay/sculpt look. Built on FIRST USE, not at construction: the matcap
|
|
14
|
+
* sphere is drawn into a 2D canvas, and every ViewportShadingRenderer that
|
|
15
|
+
* only ever draws `solid` would otherwise pay for a texture nobody samples.
|
|
16
|
+
* `toneMapped: false` for the same reason the overdraw material sets it —
|
|
17
|
+
* a diagnostic look must read exactly as authored, not as the document's
|
|
18
|
+
* exposure and tone curve happen to grade it.
|
|
19
|
+
*/
|
|
20
|
+
private _matcap;
|
|
21
|
+
private _matcapTexture;
|
|
12
22
|
private readonly _overdraw;
|
|
13
23
|
render(scene: THREE.Scene, mode: ViewportShadingMode, draw: () => void, include?: (mesh: THREE.Mesh) => boolean): void;
|
|
14
24
|
dispose(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"viewport-shading.d.ts","sourceRoot":"","sources":["../../src/render/viewport-shading.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"viewport-shading.d.ts","sourceRoot":"","sources":["../../src/render/viewport-shading.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,+EAA+E;AAC/E,MAAM,MAAM,mBAAmB,GAC3B,OAAO,GACP,MAAM,GACN,OAAO,GACP,WAAW,GACX,QAAQ,GACR,SAAS,GACT,UAAU,CAAC;AAqBf;;;;GAIG;AACH,qBAAa,uBAAuB;IAClC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA2C;IACpE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkC;IAC3D;;;;;;;OAOG;IACH,OAAO,CAAC,OAAO,CAAyC;IACxD,OAAO,CAAC,cAAc,CAA8B;IACpD,OAAO,CAAC,QAAQ,CAAC,SAAS,CASvB;IAEH,MAAM,CACJ,KAAK,EAAE,KAAK,CAAC,KAAK,EAClB,IAAI,EAAE,mBAAmB,EACzB,IAAI,EAAE,MAAM,IAAI,EAChB,OAAO,GAAE,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,KAAK,OAAoB,GAClD,IAAI;IA0BP,OAAO,IAAI,IAAI;IAef,OAAO,CAAC,YAAY;CAuDrB"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as THREE from 'three';
|
|
2
|
+
import { createNeutralMatcapTexture } from './matcap-texture';
|
|
2
3
|
function materialColor(material) {
|
|
3
4
|
const color = material.color;
|
|
4
5
|
return color?.clone() ?? new THREE.Color(0xbec7d1);
|
|
@@ -14,6 +15,16 @@ function textureOf(material, key) {
|
|
|
14
15
|
export class ViewportShadingRenderer {
|
|
15
16
|
_derived = new Map();
|
|
16
17
|
_normals = new THREE.MeshNormalMaterial();
|
|
18
|
+
/**
|
|
19
|
+
* The clay/sculpt look. Built on FIRST USE, not at construction: the matcap
|
|
20
|
+
* sphere is drawn into a 2D canvas, and every ViewportShadingRenderer that
|
|
21
|
+
* only ever draws `solid` would otherwise pay for a texture nobody samples.
|
|
22
|
+
* `toneMapped: false` for the same reason the overdraw material sets it —
|
|
23
|
+
* a diagnostic look must read exactly as authored, not as the document's
|
|
24
|
+
* exposure and tone curve happen to grade it.
|
|
25
|
+
*/
|
|
26
|
+
_matcap = null;
|
|
27
|
+
_matcapTexture = null;
|
|
17
28
|
_overdraw = new THREE.MeshBasicMaterial({
|
|
18
29
|
color: 0xffffff,
|
|
19
30
|
transparent: true,
|
|
@@ -56,12 +67,26 @@ export class ViewportShadingRenderer {
|
|
|
56
67
|
this._derived.clear();
|
|
57
68
|
this._normals.dispose();
|
|
58
69
|
this._overdraw.dispose();
|
|
70
|
+
this._matcap?.dispose();
|
|
71
|
+
this._matcapTexture?.dispose();
|
|
72
|
+
this._matcap = null;
|
|
73
|
+
this._matcapTexture = null;
|
|
59
74
|
}
|
|
60
75
|
_materialFor(material, mode) {
|
|
61
76
|
if (mode === 'normals')
|
|
62
77
|
return this._normals;
|
|
63
78
|
if (mode === 'overdraw')
|
|
64
79
|
return this._overdraw;
|
|
80
|
+
if (mode === 'matcap') {
|
|
81
|
+
if (!this._matcap) {
|
|
82
|
+
this._matcapTexture = createNeutralMatcapTexture();
|
|
83
|
+
this._matcap = new THREE.MeshMatcapMaterial({
|
|
84
|
+
matcap: this._matcapTexture,
|
|
85
|
+
toneMapped: false,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
return this._matcap;
|
|
89
|
+
}
|
|
65
90
|
let pair = this._derived.get(material);
|
|
66
91
|
if (!pair) {
|
|
67
92
|
const common = {
|
|
@@ -112,14 +112,15 @@ export async function createGameRuntime(config) {
|
|
|
112
112
|
// Universal root host
|
|
113
113
|
// ---------------------------------------------------------------------------
|
|
114
114
|
/** A stand-in `THREE.WebGLRenderer` for headless (`headless:true`) three
|
|
115
|
-
* roots:
|
|
116
|
-
*
|
|
117
|
-
* this only needs to satisfy the handful of calls THIS file itself makes
|
|
115
|
+
* roots: draw submission is inert while native scene/component evaluation
|
|
116
|
+
* still runs. This satisfies Fiber's custom-renderer gate and the calls
|
|
118
117
|
* (`setPixelRatio`/`setClearColor`/`setSize` at mount, `dispose`/
|
|
119
118
|
* `forceContextLoss` at teardown) — never a real GL call. Node conformance
|
|
120
119
|
* tests only; never used when `headless` is left `false`/absent. */
|
|
121
|
-
function createHeadlessRendererStub() {
|
|
120
|
+
function createHeadlessRendererStub(canvas) {
|
|
122
121
|
return {
|
|
122
|
+
domElement: canvas,
|
|
123
|
+
render() { },
|
|
123
124
|
setPixelRatio() { },
|
|
124
125
|
setClearColor() { },
|
|
125
126
|
setSize() { },
|
|
@@ -174,7 +175,7 @@ function disposeMountedRoot(entry, container) {
|
|
|
174
175
|
async function mountOneThreeRoot(spec, ctx) {
|
|
175
176
|
const { game, canvas, w, h, dpr, isBottom, headless, assets, antialias } = ctx;
|
|
176
177
|
const renderer = headless
|
|
177
|
-
? createHeadlessRendererStub()
|
|
178
|
+
? createHeadlessRendererStub(canvas)
|
|
178
179
|
: createHostRenderer(canvas, w, h,
|
|
179
180
|
// The manifest's `rendering.antialias` reaches the WebGL context here and NOWHERE else:
|
|
180
181
|
// `createHostRenderer` passes it straight to the constructor, and the sample count is
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mount-manifest.d.ts","sourceRoot":"","sources":["../../src/runtime/mount-manifest.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"mount-manifest.d.ts","sourceRoot":"","sources":["../../src/runtime/mount-manifest.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAE1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAG3D,OAAO,EAGL,KAAK,oBAAoB,EAC1B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAqB,KAAK,WAAW,EAAsB,MAAM,kBAAkB,CAAC;AAC3F,OAAO,EAAE,KAAK,uBAAuB,EAA2B,MAAM,gBAAgB,CAAC;AAGvF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAOlD;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,eAAgB,SAAQ,qBAAqB;IAC5D,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,OAAO,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;CAC5C;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,WAAW,CAAC,EAAE,IAAI,CAAC,eAAe,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;CAClE;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,cAAe,SAAQ,qBAAqB;IAC3D,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,QAAQ,CAAC,CAAC;CACzC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,eAAgB,SAAQ,qBAAqB;IAC5D,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IACrB,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,KAAK,CAAC,CAAC;CACtC;AAED,MAAM,MAAM,UAAU,GAAG,eAAe,GAAG,cAAc,GAAG,eAAe,CAAC;AAM5E,MAAM,WAAW,oBAAoB;IACnC;;;;;;;;OAQG;IACH,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B;6DACyD;IACzD,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAC;IAChC;;qEAEiE;IACjE,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,GAAG,SAAS,CAAC;IACpE;8EAC0E;IAC1E,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC;gEAC4D;IAC5D,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACxC;;;;;;;OAOG;IACH,QAAQ,CAAC,WAAW,CAAC,EACjB;QACE,QAAQ,CAAC,GAAG,CAAC,EAAE;YAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;SAAE,GAAG,SAAS,CAAC;QACvD,QAAQ,CAAC,MAAM,CAAC,EAAE,uBAAuB,GAAG,SAAS,CAAC;KACvD,GACD,SAAS,CAAC;IACd;;;;;;;OAOG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,qEAAqE;IACrE,QAAQ,CAAC,QAAQ,CAAC,EAAE,eAAe,GAAG,IAAI,GAAG,SAAS,CAAC;IACvD;;;kFAG8E;IAC9E,QAAQ,CAAC,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC;CAC5D;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE;IAC3C,QAAQ,CAAC,WAAW,EAAE,oBAAoB,CAAC,aAAa,CAAC,CAAC;IAC1D,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1C,QAAQ,CAAC,GAAG,EAAE;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC;CACvD,GAAG,MAAM,GAAG,SAAS,CAIrB;AAkBD,MAAM,MAAM,sBAAsB,GAAG,WAAW,CAAC;AAyCjD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,GAAG,oBAAoB,CAGlE;AAiID;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,WAAW,CAAC,CAuFzF"}
|
|
@@ -76,6 +76,15 @@ function hasRealWindow() {
|
|
|
76
76
|
function looksAlreadyResolved(value) {
|
|
77
77
|
if (typeof value !== 'object' || value === null)
|
|
78
78
|
return false;
|
|
79
|
+
// The loader's own output field is the surest mark: a raw manifest never
|
|
80
|
+
// carries it (the schema is strict). Without this, a RESOLVED manifest
|
|
81
|
+
// with `roots: []` — a models project, nothing to play — fell through to
|
|
82
|
+
// `loadGameManifest`, whose strict parse refused THIS key ("Unrecognized
|
|
83
|
+
// key: deferredConfigurationKinds") instead of saying "no roots"
|
|
84
|
+
// (blind lantern round 21, 2026-09-06, sent there by the never-played nag).
|
|
85
|
+
if (Array.isArray(value.deferredConfigurationKinds)) {
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
79
88
|
const roots = value.roots;
|
|
80
89
|
if (!Array.isArray(roots) || roots.length === 0)
|
|
81
90
|
return false;
|
|
@@ -97,7 +106,7 @@ function looksAlreadyResolved(value) {
|
|
|
97
106
|
export function resolveManifest(raw) {
|
|
98
107
|
if (looksAlreadyResolved(raw))
|
|
99
108
|
return raw;
|
|
100
|
-
return loadGameManifest(raw);
|
|
109
|
+
return loadGameManifest(raw, { configurationKinds: 'defer' });
|
|
101
110
|
}
|
|
102
111
|
// ---------------------------------------------------------------------------
|
|
103
112
|
// Per-kind entry -> adapter resolution (mirrors `resolveRootBinding`'s
|
|
@@ -265,7 +274,7 @@ export async function mountManifestRoots(opts) {
|
|
|
265
274
|
// `manifest/load.ts`) is `{ room, module } | undefined`; its mere presence
|
|
266
275
|
// is the "this project declares a room" signal, independent of
|
|
267
276
|
// whether/when the game actually joins it.
|
|
268
|
-
if (manifest.
|
|
277
|
+
if (manifest.configurations.some((configuration) => configuration.kind === 'process')) {
|
|
269
278
|
debugRegistry.setRoomDeclared(true);
|
|
270
279
|
}
|
|
271
280
|
// Defect 5 fix: `maybeInstallDebugBridge` adds `window` listeners and
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE SETTINGS DOCUMENT — the Zod truth for the two shared settings layers
|
|
3
|
+
* (ARCHITECTURE-CORE §Editor chrome, "Settings have four layers with named
|
|
4
|
+
* homes"):
|
|
5
|
+
*
|
|
6
|
+
* ~/.vgai/settings.json the USER layer — one person, every project
|
|
7
|
+
* <project>/.vgai/settings.json the PROJECT layer — one project, committed,
|
|
8
|
+
* every person who opens it
|
|
9
|
+
*
|
|
10
|
+
* Both files carry the same shape; the project layer overrides the user layer
|
|
11
|
+
* key by key (`mergeEditorSettings`). Neither layer holds authored content —
|
|
12
|
+
* an unreadable file is reported by name and falls back to defaults, never
|
|
13
|
+
* migrated. The generated JSON Schema (`npm run generate-schema` →
|
|
14
|
+
* `packages/engine/schemas/vgai-settings.schema.json`) is what an editor
|
|
15
|
+
* autocompletes against when a person edits either file by hand.
|
|
16
|
+
*
|
|
17
|
+
* Every field here has a runtime reader in the editor's settings store
|
|
18
|
+
* consumers (`theme-preference.ts`, `workspace-regions.ts`,
|
|
19
|
+
* `keymap-presets.ts`, `workspace-presets.ts`, `device-preview.ts`); a field
|
|
20
|
+
* with no reader does not get declared (CLAUDE.md, "Do not author schema
|
|
21
|
+
* fields that have no runtime reader").
|
|
22
|
+
*/
|
|
23
|
+
import { z } from 'zod';
|
|
24
|
+
export declare const ChromeRegionsSettingsSchema: z.ZodObject<{
|
|
25
|
+
tabs: z.ZodOptional<z.ZodEnum<{
|
|
26
|
+
hidden: "hidden";
|
|
27
|
+
shown: "shown";
|
|
28
|
+
}>>;
|
|
29
|
+
header: z.ZodOptional<z.ZodEnum<{
|
|
30
|
+
hidden: "hidden";
|
|
31
|
+
shown: "shown";
|
|
32
|
+
}>>;
|
|
33
|
+
shelf: z.ZodOptional<z.ZodEnum<{
|
|
34
|
+
hidden: "hidden";
|
|
35
|
+
shown: "shown";
|
|
36
|
+
}>>;
|
|
37
|
+
inspector: z.ZodOptional<z.ZodEnum<{
|
|
38
|
+
properties: "properties";
|
|
39
|
+
column: "column";
|
|
40
|
+
}>>;
|
|
41
|
+
drawer: z.ZodOptional<z.ZodEnum<{
|
|
42
|
+
hidden: "hidden";
|
|
43
|
+
shown: "shown";
|
|
44
|
+
}>>;
|
|
45
|
+
}, z.core.$strict>;
|
|
46
|
+
export declare const EditorSettingsSchema: z.ZodObject<{
|
|
47
|
+
$schema: z.ZodOptional<z.ZodString>;
|
|
48
|
+
appearance: z.ZodOptional<z.ZodObject<{
|
|
49
|
+
palette: z.ZodOptional<z.ZodString>;
|
|
50
|
+
material: z.ZodOptional<z.ZodString>;
|
|
51
|
+
regions: z.ZodOptional<z.ZodObject<{
|
|
52
|
+
tabs: z.ZodOptional<z.ZodEnum<{
|
|
53
|
+
hidden: "hidden";
|
|
54
|
+
shown: "shown";
|
|
55
|
+
}>>;
|
|
56
|
+
header: z.ZodOptional<z.ZodEnum<{
|
|
57
|
+
hidden: "hidden";
|
|
58
|
+
shown: "shown";
|
|
59
|
+
}>>;
|
|
60
|
+
shelf: z.ZodOptional<z.ZodEnum<{
|
|
61
|
+
hidden: "hidden";
|
|
62
|
+
shown: "shown";
|
|
63
|
+
}>>;
|
|
64
|
+
inspector: z.ZodOptional<z.ZodEnum<{
|
|
65
|
+
properties: "properties";
|
|
66
|
+
column: "column";
|
|
67
|
+
}>>;
|
|
68
|
+
drawer: z.ZodOptional<z.ZodEnum<{
|
|
69
|
+
hidden: "hidden";
|
|
70
|
+
shown: "shown";
|
|
71
|
+
}>>;
|
|
72
|
+
}, z.core.$strict>>;
|
|
73
|
+
}, z.core.$strict>>;
|
|
74
|
+
keymap: z.ZodOptional<z.ZodString>;
|
|
75
|
+
workspace: z.ZodOptional<z.ZodObject<{
|
|
76
|
+
default: z.ZodOptional<z.ZodString>;
|
|
77
|
+
}, z.core.$strict>>;
|
|
78
|
+
devicePreview: z.ZodOptional<z.ZodObject<{
|
|
79
|
+
preset: z.ZodOptional<z.ZodString>;
|
|
80
|
+
touch: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
|
|
81
|
+
}, z.core.$strict>>;
|
|
82
|
+
}, z.core.$strict>;
|
|
83
|
+
export type EditorSettings = z.infer<typeof EditorSettingsSchema>;
|
|
84
|
+
export type ChromeRegionsSettings = z.infer<typeof ChromeRegionsSettingsSchema>;
|
|
85
|
+
/** Which layer a file is; the file paths are the server's. */
|
|
86
|
+
export type SettingsLayer = 'user' | 'project';
|
|
87
|
+
/** Deep-merge plain objects; anything else (primitives, arrays, null)
|
|
88
|
+
* replaces. `merge(user, project)` is the effective document. */
|
|
89
|
+
export declare function mergeEditorSettings(base: EditorSettings, over: EditorSettings): EditorSettings;
|
|
90
|
+
/** Validate a raw document. The result carries the parsed settings or the
|
|
91
|
+
* issues, spelled for a person who has the file open. */
|
|
92
|
+
export declare function parseEditorSettings(raw: unknown): {
|
|
93
|
+
settings: EditorSettings;
|
|
94
|
+
issues: [];
|
|
95
|
+
} | {
|
|
96
|
+
settings: null;
|
|
97
|
+
issues: string[];
|
|
98
|
+
};
|
|
99
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/settings/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;;;;;;;;;kBAY0B,CAAC;AAEnE,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBA6CsD,CAAC;AAExF,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAClE,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAC;AAEhF,8DAA8D;AAC9D,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,SAAS,CAAC;AAM/C;kEACkE;AAClE,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,cAAc,GAAG,cAAc,CAY9F;AAED;0DAC0D;AAC1D,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,OAAO,GACX;IAAE,QAAQ,EAAE,cAAc,CAAC;IAAC,MAAM,EAAE,EAAE,CAAA;CAAE,GAAG;IAAE,QAAQ,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CASjF"}
|