@needle-tools/engine 5.1.0-canary.db0c38f → 5.1.0-canary.e9062c0
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/.needle/generated/needle-bindings.gen.d.ts +5 -0
- package/components.needle.json +1 -1
- package/dist/{needle-engine.bundle-Dq0Ly8fW.umd.cjs → needle-engine.bundle--q_w7iXI.umd.cjs} +125 -125
- package/dist/{needle-engine.bundle-B29kieh0.js → needle-engine.bundle-CDKcuALa.js} +3481 -3422
- package/dist/{needle-engine.bundle-YnpzzOPL.min.js → needle-engine.bundle-w22UkaT_.min.js} +137 -137
- package/dist/needle-engine.d.ts +61 -41
- package/dist/needle-engine.js +2 -2
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/lib/engine/api.d.ts +1 -1
- package/lib/engine/engine_context.d.ts +7 -3
- package/lib/engine/engine_context.js +16 -2
- package/lib/engine/engine_context.js.map +1 -1
- package/lib/engine/engine_init.js +5 -0
- package/lib/engine/engine_init.js.map +1 -1
- package/lib/engine/engine_license.d.ts +2 -0
- package/lib/engine/engine_license.js +14 -6
- package/lib/engine/engine_license.js.map +1 -1
- package/lib/engine/engine_lifecycle_functions_internal.js +5 -0
- package/lib/engine/engine_lifecycle_functions_internal.js.map +1 -1
- package/lib/engine/engine_pmrem.js +2 -2
- package/lib/engine/engine_pmrem.js.map +1 -1
- package/lib/engine/engine_scenedata.d.ts +8 -10
- package/lib/engine/engine_scenedata.js +44 -20
- package/lib/engine/engine_scenedata.js.map +1 -1
- package/lib/engine/engine_ssr.d.ts +2 -0
- package/lib/engine/engine_ssr.js +20 -0
- package/lib/engine/engine_ssr.js.map +1 -1
- package/lib/engine/engine_types.d.ts +2 -0
- package/lib/engine/engine_types.js.map +1 -1
- package/lib/engine/webcomponents/jsx.d.ts +44 -0
- package/lib/engine/webcomponents/logo-element.js.map +1 -1
- package/lib/engine/webcomponents/needle menu/needle-menu.d.ts +2 -3
- package/lib/engine/webcomponents/needle menu/needle-menu.js.map +1 -1
- package/lib/engine/webcomponents/needle-button.js.map +1 -1
- package/lib/engine/webcomponents/needle-engine.js.map +1 -1
- package/lib/engine-components/Light.d.ts +6 -8
- package/lib/engine-components/Light.js +38 -27
- package/lib/engine-components/Light.js.map +1 -1
- package/lib/needle-engine.d.ts +2 -0
- package/lib/needle-engine.js +2 -0
- package/lib/needle-engine.js.map +1 -1
- package/package.json +3 -2
- package/plugins/dts-generator/dts.codegen.js +255 -50
- package/plugins/dts-generator/dts.scan.js +37 -9
- package/plugins/dts-generator/dts.writer.js +1 -1
- package/plugins/dts-generator/glb.discovery.js +140 -23
- package/plugins/dts-generator/glb.extractor.js +48 -8
- package/plugins/dts-generator/glb.reader.js +80 -27
- package/plugins/dts-generator/index.js +1 -1
- package/plugins/types/needle-bindings.d.ts +25 -14
- package/plugins/types/userconfig.d.ts +12 -0
- package/plugins/vite/asap.js +1 -1
- package/plugins/vite/dependency-watcher.d.ts +2 -2
- package/plugins/vite/dependency-watcher.js +3 -4
- package/plugins/vite/drop.d.ts +2 -2
- package/plugins/vite/drop.js +3 -4
- package/plugins/vite/dts-generator.d.ts +2 -2
- package/plugins/vite/dts-generator.js +43 -9
- package/plugins/vite/index.d.ts +9 -3
- package/plugins/vite/index.js +23 -10
- package/plugins/vite/meta.js +4 -2
- package/plugins/vite/poster.d.ts +2 -2
- package/plugins/vite/poster.js +3 -5
- package/plugins/vite/reload.d.ts +2 -2
- package/plugins/vite/reload.js +4 -5
- package/src/engine/api.ts +1 -1
- package/src/engine/engine_context.ts +19 -3
- package/src/engine/engine_init.ts +4 -0
- package/src/engine/engine_license.ts +12 -10
- package/src/engine/engine_lifecycle_functions_internal.ts +7 -0
- package/src/engine/engine_pmrem.ts +3 -3
- package/src/engine/engine_scenedata.ts +41 -18
- package/src/engine/engine_ssr.ts +29 -3
- package/src/engine/engine_types.ts +2 -0
- package/src/engine/webcomponents/jsx.d.ts +44 -0
- package/src/engine/webcomponents/logo-element.ts +1 -0
- package/src/engine/webcomponents/needle menu/needle-menu.ts +2 -1
- package/src/engine/webcomponents/needle-button.ts +1 -0
- package/src/engine/webcomponents/needle-engine.ts +1 -0
- package/src/engine-components/Light.ts +39 -26
- package/src/needle-engine.ts +3 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @param {"build" | "serve"}
|
|
2
|
+
* @param {"build" | "serve" | undefined} _command
|
|
3
3
|
* @param {unknown} _config
|
|
4
4
|
* @param {import('../types').userSettings} userSettings
|
|
5
5
|
* @returns {import('vite').Plugin | null}
|
|
6
6
|
*/
|
|
7
|
-
export function needleDependencyWatcher(
|
|
7
|
+
export function needleDependencyWatcher(_command: "build" | "serve" | undefined, _config: unknown, userSettings: import("../types").userSettings): import("vite").Plugin | null;
|
|
8
8
|
export type PackageJson = {
|
|
9
9
|
dependencies?: Record<string, string>;
|
|
10
10
|
devDependencies?: Record<string, string>;
|
|
@@ -12,14 +12,12 @@ function log(...msg) {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
* @param {"build" | "serve"}
|
|
15
|
+
* @param {"build" | "serve" | undefined} _command
|
|
16
16
|
* @param {unknown} _config
|
|
17
17
|
* @param {import('../types').userSettings} userSettings
|
|
18
18
|
* @returns {import('vite').Plugin | null}
|
|
19
19
|
*/
|
|
20
|
-
export function needleDependencyWatcher(
|
|
21
|
-
if (command === "build") return null;
|
|
22
|
-
|
|
20
|
+
export function needleDependencyWatcher(_command, _config, userSettings) {
|
|
23
21
|
if (userSettings?.noDependencyWatcher === true) return null;
|
|
24
22
|
|
|
25
23
|
const dir = process.cwd();
|
|
@@ -28,6 +26,7 @@ export function needleDependencyWatcher(command, _config, userSettings) {
|
|
|
28
26
|
|
|
29
27
|
return /** @type {import('vite').Plugin} */ ({
|
|
30
28
|
name: 'needle-dependency-watcher',
|
|
29
|
+
apply: 'serve',
|
|
31
30
|
/** @param {import('vite').ViteDevServer} server */
|
|
32
31
|
configureServer(server) {
|
|
33
32
|
manageClients(server);
|
package/plugins/vite/drop.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** Experimental, allow dropping files from Unity into the running scene.
|
|
2
|
-
* @param {"build" | "serve"}
|
|
2
|
+
* @param {"build" | "serve" | undefined} _command
|
|
3
3
|
* @param {import('../types/needleConfig').needleMeta | null | undefined} config
|
|
4
4
|
* @param {import('../types/userconfig.js').userSettings} userSettings
|
|
5
5
|
* @returns {import('vite').Plugin | null | undefined}
|
|
6
6
|
*/
|
|
7
|
-
export function needleDrop(
|
|
7
|
+
export function needleDrop(_command: "build" | "serve" | undefined, config: import("../types/needleConfig").needleMeta | null | undefined, userSettings: import("../types/userconfig.js").userSettings): import("vite").Plugin | null | undefined;
|
package/plugins/vite/drop.js
CHANGED
|
@@ -7,18 +7,17 @@ const __filename = fileURLToPath(import.meta.url);
|
|
|
7
7
|
const __dirname = path.dirname(__filename);
|
|
8
8
|
|
|
9
9
|
/** Experimental, allow dropping files from Unity into the running scene.
|
|
10
|
-
* @param {"build" | "serve"}
|
|
10
|
+
* @param {"build" | "serve" | undefined} _command
|
|
11
11
|
* @param {import('../types/needleConfig').needleMeta | null | undefined} config
|
|
12
12
|
* @param {import('../types/userconfig.js').userSettings} userSettings
|
|
13
13
|
* @returns {import('vite').Plugin | null | undefined}
|
|
14
14
|
*/
|
|
15
|
-
export function needleDrop(
|
|
16
|
-
if (command === "build") return;
|
|
17
|
-
|
|
15
|
+
export function needleDrop(_command, config, userSettings) {
|
|
18
16
|
if(userSettings.useDrop !== true) return null;
|
|
19
17
|
|
|
20
18
|
return {
|
|
21
19
|
name: "needle:drop",
|
|
20
|
+
apply: 'serve',
|
|
22
21
|
config(/** @type {{ server?: { hmr?: { port?: number } } }} */ viteConfig) {
|
|
23
22
|
if(userSettings)
|
|
24
23
|
if (!viteConfig.server) viteConfig.server = {};
|
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
* @param {"build" | "serve"} _command Vite command (unused — runs in both modes)
|
|
3
3
|
* @param {import('../types/needleConfig').needleMeta | null | undefined} _config
|
|
4
4
|
* @param {import('../types').userSettings} [_userSettings]
|
|
5
|
-
* @returns {import('vite').Plugin}
|
|
5
|
+
* @returns {import('vite').Plugin | null}
|
|
6
6
|
*/
|
|
7
|
-
export function needleDtsGenerator(_command: "build" | "serve", _config: import("../types/needleConfig").needleMeta | null | undefined, _userSettings?: import("../types").userSettings): import("vite").Plugin;
|
|
7
|
+
export function needleDtsGenerator(_command: "build" | "serve", _config: import("../types/needleConfig").needleMeta | null | undefined, _userSettings?: import("../types").userSettings): import("vite").Plugin | null;
|
|
@@ -16,12 +16,24 @@
|
|
|
16
16
|
* needleDtsGenerator(command, needleConfig, userSettings)
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import { join, resolve } from 'path';
|
|
20
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
|
|
19
|
+
import { join, resolve, dirname } from 'path';
|
|
20
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, realpathSync } from 'fs';
|
|
21
21
|
import { tryLoadProjectConfig } from './config.js';
|
|
22
22
|
import { generateBindingsDts } from '../dts-generator/index.js';
|
|
23
23
|
import { needleLog } from './logging.js';
|
|
24
24
|
|
|
25
|
+
// Two dirs up from plugins/vite/ → package root.
|
|
26
|
+
// Use fileURLToPath so %7E and other URL-encoded characters in the path are decoded correctly.
|
|
27
|
+
// realpathSync follows symlinks so the path is stable even in npm link / monorepo setups.
|
|
28
|
+
import { fileURLToPath } from 'url';
|
|
29
|
+
let _packageRoot = /** @type {string | null} */ (null);
|
|
30
|
+
try {
|
|
31
|
+
_packageRoot = realpathSync(join(dirname(fileURLToPath(import.meta.url)), '..', '..'));
|
|
32
|
+
} catch (_e) {
|
|
33
|
+
// If we can't resolve the package root (e.g. unusual install layout), the plugin
|
|
34
|
+
// will silently skip generation rather than crashing the dev server.
|
|
35
|
+
}
|
|
36
|
+
|
|
25
37
|
/**
|
|
26
38
|
* Ensure `.vscode/settings.json` references the generated `needle-html-data.json`
|
|
27
39
|
* so VS Code provides `data-bind-needle` completions in HTML files automatically.
|
|
@@ -60,9 +72,14 @@ function ensureVscodeHtmlCustomData(projectRoot, htmlDataPath) {
|
|
|
60
72
|
* @param {"build" | "serve"} _command Vite command (unused — runs in both modes)
|
|
61
73
|
* @param {import('../types/needleConfig').needleMeta | null | undefined} _config
|
|
62
74
|
* @param {import('../types').userSettings} [_userSettings]
|
|
63
|
-
* @returns {import('vite').Plugin}
|
|
75
|
+
* @returns {import('vite').Plugin | null}
|
|
64
76
|
*/
|
|
65
77
|
export function needleDtsGenerator(_command, _config, _userSettings) {
|
|
78
|
+
|
|
79
|
+
if(_userSettings?.dts?.enabled === false) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
|
|
66
83
|
let projectRoot = process.cwd();
|
|
67
84
|
|
|
68
85
|
function resolveCodegenDir() {
|
|
@@ -73,7 +90,11 @@ export function needleDtsGenerator(_command, _config, _userSettings) {
|
|
|
73
90
|
}
|
|
74
91
|
|
|
75
92
|
function resolveOutputPath() {
|
|
76
|
-
|
|
93
|
+
// Write to a .needle dotfolder at the package root.
|
|
94
|
+
// Not in the `files` allowlist → never npm-published.
|
|
95
|
+
// ../../.needle/ from plugins/types/ resolves correctly for both
|
|
96
|
+
// symlinked (js/package~/) and published (node_modules/@needle-tools/engine/) layouts.
|
|
97
|
+
return join(/** @type {string} */ (_packageRoot), ".needle", "generated", "needle-bindings.gen.d.ts");
|
|
77
98
|
}
|
|
78
99
|
|
|
79
100
|
function resolveAssetsDir() {
|
|
@@ -86,14 +107,24 @@ export function needleDtsGenerator(_command, _config, _userSettings) {
|
|
|
86
107
|
/** @type {import('vite').ViteDevServer | undefined} */
|
|
87
108
|
let devServer;
|
|
88
109
|
|
|
89
|
-
|
|
110
|
+
/** @type {Promise<void> | null} */
|
|
111
|
+
let _runInFlight = null;
|
|
112
|
+
|
|
113
|
+
function run() {
|
|
114
|
+
if (_runInFlight) return _runInFlight;
|
|
115
|
+
_runInFlight = _doRun().finally(() => { _runInFlight = null; });
|
|
116
|
+
return _runInFlight;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async function _doRun() {
|
|
90
120
|
try {
|
|
121
|
+
if (!_packageRoot) return;
|
|
91
122
|
const assetsDir = resolveAssetsDir();
|
|
92
123
|
const outputPath = resolveOutputPath();
|
|
93
124
|
const codegenDir = resolveCodegenDir();
|
|
94
125
|
const count = await generateBindingsDts({ assetsDir, outputPath, projectRoot, codegenDir });
|
|
95
|
-
//
|
|
96
|
-
const htmlDataPath = join(
|
|
126
|
+
// HTML custom data sits next to the generated dts in .needle/generated/
|
|
127
|
+
const htmlDataPath = join(/** @type {string} */ (_packageRoot), ".needle", "generated", "needle-html-data.json");
|
|
97
128
|
ensureVscodeHtmlCustomData(projectRoot, htmlDataPath);
|
|
98
129
|
if (count !== false) {
|
|
99
130
|
needleLog("needle:dts-generator", `${count} binding(s) → ${outputPath.replace(process.cwd(), ".")}`);
|
|
@@ -102,7 +133,7 @@ export function needleDtsGenerator(_command, _config, _userSettings) {
|
|
|
102
133
|
hot.send({ type: "full-reload", path: "*" });
|
|
103
134
|
}
|
|
104
135
|
} else {
|
|
105
|
-
needleLog("needle:dts-generator", `up-to-date`);
|
|
136
|
+
needleLog("needle:dts-generator", `up-to-date → ${outputPath.replace(process.cwd(), ".")}`);
|
|
106
137
|
}
|
|
107
138
|
} catch (err) {
|
|
108
139
|
needleLog("needle:dts-generator", "Failed: " + (/** @type {any} */ (err)?.message ?? err));
|
|
@@ -118,7 +149,10 @@ export function needleDtsGenerator(_command, _config, _userSettings) {
|
|
|
118
149
|
},
|
|
119
150
|
|
|
120
151
|
buildStart() {
|
|
121
|
-
|
|
152
|
+
// In serve mode, the configureServer post-hook runs instead.
|
|
153
|
+
// Only run here for actual builds (no devServer).
|
|
154
|
+
if (!devServer) return run();
|
|
155
|
+
return undefined;
|
|
122
156
|
},
|
|
123
157
|
|
|
124
158
|
/** @param {import('vite').ViteDevServer} server */
|
package/plugins/vite/index.d.ts
CHANGED
|
@@ -19,12 +19,18 @@
|
|
|
19
19
|
* ],
|
|
20
20
|
* }
|
|
21
21
|
* ```
|
|
22
|
-
*
|
|
23
|
-
*
|
|
22
|
+
* When used without arguments (e.g. in a SvelteKit `defineConfig({})` plain object),
|
|
23
|
+
* the command is resolved automatically from Vite's `configResolved` hook.
|
|
24
|
+
* ```js
|
|
25
|
+
* // SvelteKit — no command needed:
|
|
26
|
+
* export default defineConfig({ plugins: [sveltekit(), needlePlugins()] });
|
|
27
|
+
* ```
|
|
28
|
+
* @param {"build" | "serve" | undefined} [command]
|
|
29
|
+
* @param {import('../types/needleConfig').needleMeta | null | undefined} [config]
|
|
24
30
|
* @param {import('../types/index.js').userSettings} [userSettings]
|
|
25
31
|
* @returns {Promise<import('vite').Plugin[]>}
|
|
26
32
|
*/
|
|
27
|
-
export function needlePlugins(command
|
|
33
|
+
export function needlePlugins(command?: "build" | "serve" | undefined, config?: import("../types/needleConfig").needleMeta | null | undefined, userSettings?: import("../types/index.js").userSettings): Promise<import("vite").Plugin[]>;
|
|
28
34
|
export { needleAI } from "./ai.js";
|
|
29
35
|
export { needleDtsGenerator } from "./dts-generator.js";
|
|
30
36
|
export { needleAsap } from "./asap.js";
|
package/plugins/vite/index.js
CHANGED
|
@@ -116,12 +116,18 @@ const defaultUserSettings = {
|
|
|
116
116
|
* ],
|
|
117
117
|
* }
|
|
118
118
|
* ```
|
|
119
|
-
*
|
|
120
|
-
*
|
|
119
|
+
* When used without arguments (e.g. in a SvelteKit `defineConfig({})` plain object),
|
|
120
|
+
* the command is resolved automatically from Vite's `configResolved` hook.
|
|
121
|
+
* ```js
|
|
122
|
+
* // SvelteKit — no command needed:
|
|
123
|
+
* export default defineConfig({ plugins: [sveltekit(), needlePlugins()] });
|
|
124
|
+
* ```
|
|
125
|
+
* @param {"build" | "serve" | undefined} [command]
|
|
126
|
+
* @param {import('../types/needleConfig').needleMeta | null | undefined} [config]
|
|
121
127
|
* @param {import('../types/index.js').userSettings} [userSettings]
|
|
122
128
|
* @returns {Promise<import('vite').Plugin[]>}
|
|
123
129
|
*/
|
|
124
|
-
export async function needlePlugins(command, config = undefined, userSettings = {}) {
|
|
130
|
+
export async function needlePlugins(command = undefined, config = undefined, userSettings = {}) {
|
|
125
131
|
|
|
126
132
|
if (!config) config = {}
|
|
127
133
|
|
|
@@ -168,14 +174,21 @@ export async function needlePlugins(command, config = undefined, userSettings =
|
|
|
168
174
|
array.push(await editorConnection(command, config, userSettings, array));
|
|
169
175
|
array.push(needleDependencyWatcher(command, config, userSettings));
|
|
170
176
|
|
|
171
|
-
// Ensure the process exits on SIGINT (Ctrl+C) since plugin timers/sockets can keep the event loop alive
|
|
172
|
-
|
|
177
|
+
// Ensure the process exits on SIGINT (Ctrl+C) since plugin timers/sockets can keep the event loop alive.
|
|
178
|
+
// If command is unknown at call time (e.g. SvelteKit plain defineConfig), defer to configResolved.
|
|
179
|
+
const registerSigint = () => {
|
|
173
180
|
process.on('SIGINT', () => {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
181
|
+
setTimeout(() => { console.debug('\nGoodbye!'); process.exit(); }, 1000);
|
|
182
|
+
});
|
|
183
|
+
};
|
|
184
|
+
if (command === "serve") {
|
|
185
|
+
registerSigint();
|
|
186
|
+
} else if (command === undefined) {
|
|
187
|
+
array.push({
|
|
188
|
+
name: "needle:sigint",
|
|
189
|
+
configResolved(config) {
|
|
190
|
+
if (config.command === "serve") registerSigint();
|
|
191
|
+
}
|
|
179
192
|
});
|
|
180
193
|
}
|
|
181
194
|
|
package/plugins/vite/meta.js
CHANGED
|
@@ -13,8 +13,7 @@ import { needleGreenBold, needleLog } from './logging.js';
|
|
|
13
13
|
*/
|
|
14
14
|
export function needleMeta(command, config, userSettings) {
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
const isBuild = command === 'build';
|
|
16
|
+
let isBuild = command === 'build';
|
|
18
17
|
|
|
19
18
|
async function updateConfig() {
|
|
20
19
|
config = await loadConfig();
|
|
@@ -25,6 +24,9 @@ export function needleMeta(command, config, userSettings) {
|
|
|
25
24
|
return {
|
|
26
25
|
// replace meta tags
|
|
27
26
|
name: 'needle:meta',
|
|
27
|
+
configResolved(resolvedConfig) {
|
|
28
|
+
isBuild = resolvedConfig.command === 'build';
|
|
29
|
+
},
|
|
28
30
|
transformIndexHtml: {
|
|
29
31
|
order: 'pre',
|
|
30
32
|
handler(/** @type {string} */ html, /** @type {unknown} */ _ctx) {
|
package/plugins/vite/poster.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export function getPosterPath(): string;
|
|
2
2
|
/**
|
|
3
|
-
* @param {"build" | "serve"}
|
|
3
|
+
* @param {"build" | "serve" | undefined} _command
|
|
4
4
|
* @param {import('../types/needleConfig').needleMeta | null | undefined} config
|
|
5
5
|
* @param {import('../types').userSettings} userSettings
|
|
6
6
|
* @returns {import('vite').Plugin | undefined}
|
|
7
7
|
*/
|
|
8
|
-
export function needlePoster(
|
|
8
|
+
export function needlePoster(_command: "build" | "serve" | undefined, config: import("../types/needleConfig").needleMeta | null | undefined, userSettings: import("../types").userSettings): import("vite").Plugin | undefined;
|
package/plugins/vite/poster.js
CHANGED
|
@@ -10,19 +10,17 @@ export function getPosterPath() {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
* @param {"build" | "serve"}
|
|
13
|
+
* @param {"build" | "serve" | undefined} _command
|
|
14
14
|
* @param {import('../types/needleConfig').needleMeta | null | undefined} config
|
|
15
15
|
* @param {import('../types').userSettings} userSettings
|
|
16
16
|
* @returns {import('vite').Plugin | undefined}
|
|
17
17
|
*/
|
|
18
|
-
export function needlePoster(
|
|
19
|
-
// only relevant for local development
|
|
20
|
-
if (command === 'build') return [];
|
|
21
|
-
|
|
18
|
+
export function needlePoster(_command, config, userSettings) {
|
|
22
19
|
if (userSettings.noPoster) return;
|
|
23
20
|
|
|
24
21
|
return {
|
|
25
22
|
name: 'needle:poster',
|
|
23
|
+
apply: 'serve',
|
|
26
24
|
configureServer(server) {
|
|
27
25
|
const hot = server.hot ?? server.ws;
|
|
28
26
|
hot.on('needle:screenshot', async (data, client) => {
|
package/plugins/vite/reload.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @param {"build" | "serve"}
|
|
2
|
+
* @param {"build" | "serve" | undefined} _command
|
|
3
3
|
* @param {import('../types/needleConfig').needleMeta | null} config
|
|
4
4
|
* @param {import('../types').userSettings} userSettings
|
|
5
5
|
* @returns {import('vite').Plugin | undefined}
|
|
6
6
|
*/
|
|
7
|
-
export function needleReload(
|
|
7
|
+
export function needleReload(_command: "build" | "serve" | undefined, config: import("../types/needleConfig").needleMeta | null, userSettings: import("../types").userSettings): import("vite").Plugin | undefined;
|
package/plugins/vite/reload.js
CHANGED
|
@@ -15,14 +15,12 @@ const filesUsingHotReload = new Set();
|
|
|
15
15
|
let assetsDirectory = "";
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
|
-
* @param {"build" | "serve"}
|
|
18
|
+
* @param {"build" | "serve" | undefined} _command
|
|
19
19
|
* @param {import('../types/needleConfig').needleMeta | null} config
|
|
20
20
|
* @param {import('../types').userSettings} userSettings
|
|
21
21
|
* @returns {import('vite').Plugin | undefined}
|
|
22
22
|
*/
|
|
23
|
-
export function needleReload(
|
|
24
|
-
if (command === "build") return undefined;
|
|
25
|
-
|
|
23
|
+
export function needleReload(_command, config, userSettings) {
|
|
26
24
|
if (userSettings?.noReload === true) return undefined;
|
|
27
25
|
|
|
28
26
|
|
|
@@ -51,6 +49,7 @@ export function needleReload(command, config, userSettings) {
|
|
|
51
49
|
|
|
52
50
|
return {
|
|
53
51
|
name: 'needle:reload',
|
|
52
|
+
apply: 'serve',
|
|
54
53
|
/** @param {import('vite').UserConfig} config */
|
|
55
54
|
config(config) {
|
|
56
55
|
if (!config.server) config.server = { watch: { ignored: [] } };
|
|
@@ -174,7 +173,7 @@ async function handleReload({ file, server, modules: _modules, read, buildDirect
|
|
|
174
173
|
// meaning if its the first export after the server starts those will not trigger a reload
|
|
175
174
|
// Ignore d.ts
|
|
176
175
|
const shouldCheckIfContentChanged = file.includes("/codegen/") || file.includes("/generated/") || file.endsWith("gen.js");// || file.endsWith(".glb") || file.endsWith(".gltf") || file.endsWith(".bin");
|
|
177
|
-
if (
|
|
176
|
+
if (!file.includes("/.svelte-kit/") && !file.endsWith(".d.ts") && !file.endsWith("needle-html-data.json") && shouldCheckIfContentChanged) {
|
|
178
177
|
if (reloadIsScheduled) {
|
|
179
178
|
return [];
|
|
180
179
|
}
|
package/src/engine/api.ts
CHANGED
|
@@ -450,4 +450,4 @@ export * from "./xr/api.js"
|
|
|
450
450
|
* import type { SceneData } from "@needle-tools/engine";
|
|
451
451
|
* type OrbitSettings = SceneData["Camera"]["OrbitControls"];
|
|
452
452
|
*/
|
|
453
|
-
export type { SceneData } from "needle
|
|
453
|
+
export type { SceneData } from "needle-bindings";
|
|
@@ -38,7 +38,7 @@ import { Physics } from './engine_physics.js';
|
|
|
38
38
|
import { PlayerViewManager } from './engine_playerview.js';
|
|
39
39
|
import { RendererData as SceneLighting } from './engine_scenelighting.js';
|
|
40
40
|
import { getSceneData } from './engine_scenedata.js';
|
|
41
|
-
import type { SceneData } from 'needle
|
|
41
|
+
import type { SceneData } from 'needle-bindings';
|
|
42
42
|
import { getTempColor, logHierarchy } from './engine_three_utils.js';
|
|
43
43
|
import { Time } from './engine_time.js';
|
|
44
44
|
import { patchTonemapping } from './engine_tonemapping.js';
|
|
@@ -542,8 +542,22 @@ export class Context implements IContext {
|
|
|
542
542
|
connection: NetworkConnection;
|
|
543
543
|
/** @deprecated AssetDatabase is deprecated */
|
|
544
544
|
assets: AssetDatabase;
|
|
545
|
-
|
|
546
|
-
|
|
545
|
+
|
|
546
|
+
/** All registered lights in the scene, maintained by the Light component */
|
|
547
|
+
readonly lights: Set<ILight> = new Set();
|
|
548
|
+
|
|
549
|
+
/** The brightest registered directional light, or null if none are registered
|
|
550
|
+
* @see lights
|
|
551
|
+
*/
|
|
552
|
+
get mainLight(): ILight | null {
|
|
553
|
+
let best: ILight | null = null;
|
|
554
|
+
for (const light of this.lights) {
|
|
555
|
+
if (light.type !== "directional") continue;
|
|
556
|
+
if (!best || light.intensity > best.intensity) best = light;
|
|
557
|
+
}
|
|
558
|
+
return best;
|
|
559
|
+
}
|
|
560
|
+
|
|
547
561
|
/** @deprecated Use sceneLighting */
|
|
548
562
|
get rendererData() { return this.sceneLighting }
|
|
549
563
|
/** Access the scene lighting manager to control lighting settings in the context */
|
|
@@ -858,6 +872,8 @@ export class Context implements IContext {
|
|
|
858
872
|
this._onBeforeRenderListeners.clear();
|
|
859
873
|
this._onAfterRenderListeners.clear();
|
|
860
874
|
|
|
875
|
+
this.lights.clear();
|
|
876
|
+
|
|
861
877
|
if (!this.isManagedExternally) {
|
|
862
878
|
if (this.renderer) {
|
|
863
879
|
this.renderer.renderLists.dispose();
|
|
@@ -11,6 +11,8 @@ import { initCameraExtensions } from "./js-extensions/Camera.js";
|
|
|
11
11
|
import { patchLayers } from "./js-extensions/Layers.js";
|
|
12
12
|
import { initObject3DExtensions } from "./js-extensions/Object3D.js";
|
|
13
13
|
import { initVectorExtensions } from "./js-extensions/Vector.js";
|
|
14
|
+
import { SSR } from "./engine_ssr.js";
|
|
15
|
+
import { initLicense } from "./engine_license.js";
|
|
14
16
|
import { initWebComponents } from "./webcomponents/init.js";
|
|
15
17
|
import { initPhysics } from "./engine_physics_rapier.js";
|
|
16
18
|
import { initXR } from "./xr/init.js";
|
|
@@ -28,6 +30,7 @@ let initialized = false;
|
|
|
28
30
|
*/
|
|
29
31
|
export function initEngine() {
|
|
30
32
|
if (initialized) return;
|
|
33
|
+
if (SSR) return;
|
|
31
34
|
initialized = true;
|
|
32
35
|
|
|
33
36
|
initWebComponents();
|
|
@@ -46,4 +49,5 @@ export function initEngine() {
|
|
|
46
49
|
initSceneSwitcherAttributes();
|
|
47
50
|
initPhysics();
|
|
48
51
|
initXR();
|
|
52
|
+
initLicense();
|
|
49
53
|
}
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
import { dof } from "three/src/nodes/TSL.js";
|
|
2
|
-
|
|
3
|
-
import { isDevEnvironment } from "./debug/index.js";
|
|
4
1
|
import { BUILD_TIME, GENERATOR, PUBLIC_KEY, VERSION } from "./engine_constants.js";
|
|
5
2
|
import { ContextEvent, ContextRegistry } from "./engine_context_registry.js";
|
|
6
3
|
import { onInitialized } from "./engine_lifecycle_api.js";
|
|
@@ -8,7 +5,7 @@ import { isLocalNetwork } from "./engine_networking_utils.js";
|
|
|
8
5
|
import { Context } from "./engine_setup.js";
|
|
9
6
|
import type { IContext } from "./engine_types.js";
|
|
10
7
|
import { getParam } from "./engine_utils.js";
|
|
11
|
-
import {
|
|
8
|
+
import { SSR } from "./engine_ssr.js";
|
|
12
9
|
|
|
13
10
|
const debug = getParam("debuglicense");
|
|
14
11
|
|
|
@@ -90,7 +87,9 @@ export namespace Telemetry {
|
|
|
90
87
|
});
|
|
91
88
|
}
|
|
92
89
|
|
|
93
|
-
|
|
90
|
+
export function init() {
|
|
91
|
+
if(!SSR) onInitialized((ctx => sendPageViewEvent(ctx)), { once: true });
|
|
92
|
+
}
|
|
94
93
|
|
|
95
94
|
function sendPageViewEvent(ctx: IContext): Promise<void> | void {
|
|
96
95
|
if (!isAllowed(ctx)) {
|
|
@@ -243,11 +242,14 @@ export namespace Telemetry {
|
|
|
243
242
|
}
|
|
244
243
|
|
|
245
244
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
245
|
+
export function initLicense() {
|
|
246
|
+
Telemetry.init();
|
|
247
|
+
ContextRegistry.registerCallback(ContextEvent.ContextRegistered, evt => {
|
|
248
|
+
showLicenseInfo(evt.context);
|
|
249
|
+
handleForbidden(evt.context);
|
|
250
|
+
setTimeout(() => sendUsageMessageToAnalyticsBackend(evt.context), 2000);
|
|
251
|
+
});
|
|
252
|
+
}
|
|
251
253
|
|
|
252
254
|
export let runtimeLicenseCheckPromise: Promise<void> | undefined = undefined;
|
|
253
255
|
let applicationIsForbidden = false;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type Context, FrameEvent } from "./engine_context.js";
|
|
2
2
|
import { ContextEvent } from "./engine_context_registry.js";
|
|
3
|
+
import { SSR } from "./engine_ssr.js";
|
|
3
4
|
|
|
4
5
|
export declare type Event = ContextEvent | FrameEvent;
|
|
5
6
|
|
|
@@ -38,6 +39,12 @@ let methodsWarningCounter = 0;
|
|
|
38
39
|
* @param evt the event to call the function at
|
|
39
40
|
*/
|
|
40
41
|
export function registerFrameEventCallback(cb: LifecycleMethod, evt: Event, opts?: LifecycleMethodOptions) {
|
|
42
|
+
|
|
43
|
+
// Don't register the callback if we are in SSR, because it will never be called and might cause memory leaks
|
|
44
|
+
if(SSR) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
41
48
|
if (!newMethods.has(evt)) {
|
|
42
49
|
newMethods.set(evt, new Array());
|
|
43
50
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { createLoaders } from "@needle-tools/gltf-progressive";
|
|
2
2
|
import { CubeUVReflectionMapping, EquirectangularRefractionMapping, SRGBColorSpace, Texture, TextureLoader, WebGLRenderer } from "three";
|
|
3
|
-
import { EXRLoader } from "three/examples/jsm/loaders/EXRLoader";
|
|
4
|
-
import { KTX2Loader } from "three/examples/jsm/loaders/KTX2Loader";
|
|
5
|
-
import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader";
|
|
3
|
+
import { EXRLoader } from "three/examples/jsm/loaders/EXRLoader.js";
|
|
4
|
+
import { KTX2Loader } from "three/examples/jsm/loaders/KTX2Loader.js";
|
|
5
|
+
import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader.js";
|
|
6
6
|
|
|
7
7
|
const running: Map<string, Promise<Texture | null>> = new Map();
|
|
8
8
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { SceneData } from "needle
|
|
1
|
+
import type { SceneData } from "needle-bindings";
|
|
2
|
+
export type { SceneData };
|
|
2
3
|
import type { IContext } from "./engine_types.js";
|
|
3
4
|
import { getComponent } from "./engine_components.js";
|
|
4
5
|
import { TypeStore } from "./engine_typestore.js";
|
|
@@ -70,36 +71,58 @@ function makeErrorProxy(message: string): object {
|
|
|
70
71
|
return new Proxy({}, handler);
|
|
71
72
|
}
|
|
72
73
|
|
|
74
|
+
/**
|
|
75
|
+
* Returns a proxy for a scene node that exposes `$object`, `$components`,
|
|
76
|
+
* and child nodes as nested properties.
|
|
77
|
+
*/
|
|
78
|
+
function makeNodeProxy(ctx: IContext, node: import("three").Object3D): object {
|
|
79
|
+
return new Proxy({}, {
|
|
80
|
+
get(_t, prop: string) {
|
|
81
|
+
if (prop === "$object") return node;
|
|
82
|
+
if (prop === "$components") {
|
|
83
|
+
return new Proxy({}, {
|
|
84
|
+
get(_t2, compName: string) {
|
|
85
|
+
if (compName === "then") return undefined;
|
|
86
|
+
const ctor = TypeStore.get(compName);
|
|
87
|
+
if (!ctor) return makeErrorProxy(`Component type "${compName}" not registered (node "${node.name}")`);
|
|
88
|
+
const comp = getComponent(node, ctor);
|
|
89
|
+
if (!comp) return makeErrorProxy(`Component "${compName}" not found on node "${node.name}"`);
|
|
90
|
+
return comp;
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
// Child node lookup by name
|
|
95
|
+
const child = node.children.find(c => c.name === prop) ?? null;
|
|
96
|
+
if (!child) return makeErrorProxy(`Child "${prop}" not found on node "${node.name}"`);
|
|
97
|
+
return makeNodeProxy(ctx, child);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
73
102
|
/**
|
|
74
103
|
* Returns a lazily-resolved proxy typed as {@link SceneData}.
|
|
75
104
|
* The proxy is cached per context — each context gets exactly one instance.
|
|
76
105
|
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
* If a node or component is not found, property accesses log a warning
|
|
83
|
-
* instead of throwing, so chained access never crashes.
|
|
106
|
+
* Shape mirrors the generated `needle-bindings.gen.d.ts`:
|
|
107
|
+
* ctx.sceneData.MyGlb.Camera.$components.OrbitControls.autoRotate = true;
|
|
108
|
+
* ctx.sceneData.MyGlb.Camera.$object // → THREE.Camera
|
|
109
|
+
* ctx.sceneData.MyGlb.UI.Button.$components.Button // → Needle Button component
|
|
84
110
|
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
111
|
+
* GLB name is ignored at runtime (scene is already loaded).
|
|
112
|
+
* Node lookup starts at the scene root.
|
|
87
113
|
*/
|
|
88
114
|
export function getSceneData(ctx: IContext): SceneData {
|
|
89
115
|
let proxy = cache.get(ctx);
|
|
90
116
|
if (!proxy) {
|
|
91
117
|
proxy = new Proxy({} as SceneData, {
|
|
92
|
-
get(_target,
|
|
118
|
+
get(_target, _glbName: string) {
|
|
119
|
+
// GLB name level — ignored at runtime, return node-name proxy
|
|
93
120
|
return new Proxy({}, {
|
|
94
|
-
get(_target,
|
|
121
|
+
get(_target, nodeName: string) {
|
|
122
|
+
if (nodeName === "then") return undefined;
|
|
95
123
|
const node = ctx.scene.getObjectByName(nodeName) ?? null;
|
|
96
124
|
if (!node) return makeErrorProxy(`Node "${nodeName}" not found in scene`);
|
|
97
|
-
|
|
98
|
-
const ctor = TypeStore.get(compName);
|
|
99
|
-
if (!ctor) return makeErrorProxy(`Component type "${compName}" not registered (node "${nodeName}")`);
|
|
100
|
-
const comp = getComponent(node, ctor);
|
|
101
|
-
if (!comp) return makeErrorProxy(`Component "${compName}" not found on node "${nodeName}"`);
|
|
102
|
-
return comp;
|
|
125
|
+
return makeNodeProxy(ctx, node);
|
|
103
126
|
}
|
|
104
127
|
});
|
|
105
128
|
}
|