@found-in-space/skykit 0.2.0-alpha.0 → 0.2.0-dev.20260527.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 +223 -8
- package/examples/custom-object-layer/custom-object-layer.js +1 -24
- package/examples/xr-free-roam/index.html +62 -4
- package/examples/xr-free-roam/xr-free-roam.css +249 -18
- package/examples/xr-free-roam/xr-free-roam.js +644 -217
- package/package.json +46 -5
- package/src/__tests__/skykit-anchored-images.test.js +32 -4
- package/src/__tests__/skykit-browser.test.js +442 -0
- package/src/__tests__/skykit-data.test.js +131 -0
- package/src/__tests__/skykit-parallax.test.js +4 -4
- package/src/__tests__/skykit-touch-os.test.js +71 -0
- package/src/__tests__/skykit-xr.test.js +123 -2
- package/src/__tests__/skykit.test.js +138 -1
- package/src/anchored-images.js +14 -15
- package/src/browser-addons.d.ts +16 -0
- package/src/browser-addons.js +155 -0
- package/src/browser-constellations.d.ts +13 -0
- package/src/browser-constellations.js +387 -0
- package/src/browser-journey.d.ts +8 -0
- package/src/browser-journey.js +240 -0
- package/src/browser.d.ts +170 -0
- package/src/browser.js +369 -0
- package/src/data.d.ts +133 -0
- package/src/data.js +447 -0
- package/src/embed.d.ts +6 -0
- package/src/embed.js +119 -0
- package/src/hr-diagram.js +23 -5
- package/src/index.d.ts +32 -7
- package/src/plugins.js +87 -43
- package/src/story.d.ts +57 -0
- package/src/story.js +396 -0
- package/src/three-shim.d.ts +32 -0
- package/src/touch-os.d.ts +70 -0
- package/src/touch-os.js +275 -0
- package/src/utils.js +96 -6
- package/src/viewer-entry.d.ts +10 -0
- package/src/viewer-entry.js +4 -0
- package/src/viewer.js +110 -12
- package/src/xr/plugins.js +224 -13
- package/src/xr/session.js +60 -14
- package/src/xr.d.ts +22 -0
- package/src/xr.js +1 -0
package/package.json
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@found-in-space/skykit",
|
|
3
|
-
"version": "0.2.0-
|
|
3
|
+
"version": "0.2.0-dev.20260527.0",
|
|
4
4
|
"description": "Slim composition and teaching layer for Found in Space packages",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/Found-in-Space/skykit",
|
|
10
|
+
"directory": "packages/skykit"
|
|
11
|
+
},
|
|
7
12
|
"publishConfig": {
|
|
8
13
|
"access": "public"
|
|
9
14
|
},
|
|
@@ -14,6 +19,38 @@
|
|
|
14
19
|
"types": "./src/index.d.ts",
|
|
15
20
|
"default": "./src/index.js"
|
|
16
21
|
},
|
|
22
|
+
"./browser": {
|
|
23
|
+
"types": "./src/browser.d.ts",
|
|
24
|
+
"default": "./src/browser.js"
|
|
25
|
+
},
|
|
26
|
+
"./browser-addons": {
|
|
27
|
+
"types": "./src/browser-addons.d.ts",
|
|
28
|
+
"default": "./src/browser-addons.js"
|
|
29
|
+
},
|
|
30
|
+
"./browser-constellations": {
|
|
31
|
+
"types": "./src/browser-constellations.d.ts",
|
|
32
|
+
"default": "./src/browser-constellations.js"
|
|
33
|
+
},
|
|
34
|
+
"./browser-journey": {
|
|
35
|
+
"types": "./src/browser-journey.d.ts",
|
|
36
|
+
"default": "./src/browser-journey.js"
|
|
37
|
+
},
|
|
38
|
+
"./embed": {
|
|
39
|
+
"types": "./src/embed.d.ts",
|
|
40
|
+
"default": "./src/embed.js"
|
|
41
|
+
},
|
|
42
|
+
"./viewer": {
|
|
43
|
+
"types": "./src/viewer-entry.d.ts",
|
|
44
|
+
"default": "./src/viewer-entry.js"
|
|
45
|
+
},
|
|
46
|
+
"./data": {
|
|
47
|
+
"types": "./src/data.d.ts",
|
|
48
|
+
"default": "./src/data.js"
|
|
49
|
+
},
|
|
50
|
+
"./story": {
|
|
51
|
+
"types": "./src/story.d.ts",
|
|
52
|
+
"default": "./src/story.js"
|
|
53
|
+
},
|
|
17
54
|
"./parallax": {
|
|
18
55
|
"types": "./src/parallax.d.ts",
|
|
19
56
|
"default": "./src/parallax.js"
|
|
@@ -32,7 +69,10 @@
|
|
|
32
69
|
"examples",
|
|
33
70
|
"README.md"
|
|
34
71
|
],
|
|
35
|
-
"sideEffects":
|
|
72
|
+
"sideEffects": [
|
|
73
|
+
"./src/embed.js",
|
|
74
|
+
"./src/story.js"
|
|
75
|
+
],
|
|
36
76
|
"scripts": {
|
|
37
77
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
38
78
|
"test": "node --test"
|
|
@@ -41,16 +81,17 @@
|
|
|
41
81
|
"@found-in-space/anchored-image": "0.2.0-alpha.0",
|
|
42
82
|
"@found-in-space/hr-diagram": "0.2.0-alpha.0",
|
|
43
83
|
"@found-in-space/journey": "0.2.0-alpha.0",
|
|
44
|
-
"@found-in-space/
|
|
84
|
+
"@found-in-space/meta-sidecar-provider": "0.2.0-alpha.0",
|
|
85
|
+
"@found-in-space/spatial": "0.2.0-dev.20260527.0",
|
|
45
86
|
"@found-in-space/star-octree-provider": "0.2.0-alpha.0",
|
|
46
87
|
"@found-in-space/star-trees": "0.2.0-alpha.0",
|
|
47
88
|
"@found-in-space/three-star-field": "0.2.0-alpha.0"
|
|
48
89
|
},
|
|
49
90
|
"devDependencies": {
|
|
50
|
-
"@found-in-space/touch-os": "0.2.0-dev.
|
|
91
|
+
"@found-in-space/touch-os": "0.2.0-dev.3"
|
|
51
92
|
},
|
|
52
93
|
"peerDependencies": {
|
|
53
|
-
"@found-in-space/touch-os": ">=0.2.0-dev.
|
|
94
|
+
"@found-in-space/touch-os": ">=0.2.0-dev.3 <1",
|
|
54
95
|
"three": "^0.170.0"
|
|
55
96
|
},
|
|
56
97
|
"peerDependenciesMeta": {
|
|
@@ -147,7 +147,7 @@ test('anchored image sky plugin preloads controller selection and mounts fixed-a
|
|
|
147
147
|
});
|
|
148
148
|
const viewer = await createSkykitViewer({
|
|
149
149
|
renderer: createRenderer(),
|
|
150
|
-
view: {
|
|
150
|
+
view: { lookAt: { raDeg: 0, decDeg: 0 } },
|
|
151
151
|
plugins: [plugin],
|
|
152
152
|
});
|
|
153
153
|
|
|
@@ -161,6 +161,34 @@ test('anchored image sky plugin preloads controller selection and mounts fixed-a
|
|
|
161
161
|
await viewer.dispose();
|
|
162
162
|
});
|
|
163
163
|
|
|
164
|
+
test('anchored image sky plugin can mount art into a named scale band', async () => {
|
|
165
|
+
const catalog = await createAnchoredImageCatalog({ manifest: MANIFEST });
|
|
166
|
+
const requests = [];
|
|
167
|
+
const scaleRoot = new THREE.Group();
|
|
168
|
+
const plugin = createAnchoredImageSkyPlugin({
|
|
169
|
+
id: 'banded-art',
|
|
170
|
+
catalog,
|
|
171
|
+
controller: createManualAnchoredImageController({ selection: 'alpha' }),
|
|
172
|
+
loading: 'preload',
|
|
173
|
+
textureLoader: createTextureLoader(requests),
|
|
174
|
+
anchorMode: 'scale-banded',
|
|
175
|
+
scaleBandId: 'constellation-art',
|
|
176
|
+
});
|
|
177
|
+
const viewer = await createSkykitViewer({
|
|
178
|
+
renderer: createRenderer(),
|
|
179
|
+
roots: {
|
|
180
|
+
scaleBandedContentRoots: new Map([['constellation-art', scaleRoot]]),
|
|
181
|
+
},
|
|
182
|
+
view: { lookAt: { raDeg: 0, decDeg: 0 } },
|
|
183
|
+
plugins: [plugin],
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
assert.deepEqual(requests, ['alpha.png']);
|
|
187
|
+
assert.equal(scaleRoot.children.some((child) => child.name === 'banded-art'), true);
|
|
188
|
+
|
|
189
|
+
await viewer.dispose();
|
|
190
|
+
});
|
|
191
|
+
|
|
164
192
|
test('anchored image sky plugin lazy-loads active controller entries and caches them', async () => {
|
|
165
193
|
const catalog = await createAnchoredImageCatalog({ manifest: MANIFEST });
|
|
166
194
|
const requests = [];
|
|
@@ -174,13 +202,13 @@ test('anchored image sky plugin lazy-loads active controller entries and caches
|
|
|
174
202
|
});
|
|
175
203
|
const viewer = await createSkykitViewer({
|
|
176
204
|
renderer: createRenderer(),
|
|
177
|
-
view: {
|
|
205
|
+
view: { lookAt: { raDeg: 0, decDeg: 0 } },
|
|
178
206
|
plugins: [plugin],
|
|
179
207
|
});
|
|
180
208
|
await flushPromises();
|
|
181
209
|
|
|
182
210
|
assert.deepEqual(requests, ['alpha.png']);
|
|
183
|
-
viewer.requestViewState({
|
|
211
|
+
viewer.requestViewState({ lookAt: { raDeg: 90, decDeg: 0 } }, 'test-active-change');
|
|
184
212
|
viewer.update(0);
|
|
185
213
|
await flushPromises();
|
|
186
214
|
assert.deepEqual(requests, ['alpha.png', 'beta.png']);
|
|
@@ -208,7 +236,7 @@ test('anchored image sky plugin fades opacity by seconds and keeps fading object
|
|
|
208
236
|
});
|
|
209
237
|
const viewer = await createSkykitViewer({
|
|
210
238
|
renderer: createRenderer(),
|
|
211
|
-
view: {
|
|
239
|
+
view: { lookAt: { raDeg: 0, decDeg: 0 } },
|
|
212
240
|
plugins: [plugin],
|
|
213
241
|
});
|
|
214
242
|
await flushPromises();
|
|
@@ -0,0 +1,442 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
import * as THREE from 'three';
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
installSkykitBrowserGlobal,
|
|
7
|
+
registerBrowserInstance,
|
|
8
|
+
} from '../browser-addons.js';
|
|
9
|
+
import { createSkykitBrowser } from '../browser.js';
|
|
10
|
+
|
|
11
|
+
test('createSkykitBrowser wires the starter viewer and extra plugins', async () => {
|
|
12
|
+
await withFakeWindow(async (fakeWindow) => {
|
|
13
|
+
const host = createHost();
|
|
14
|
+
const status = { textContent: '' };
|
|
15
|
+
const renderer = createRenderer();
|
|
16
|
+
const provider = createProvider();
|
|
17
|
+
const starField = createStarField();
|
|
18
|
+
const extraPartCalls = [];
|
|
19
|
+
|
|
20
|
+
const browser = await createSkykitBrowser({
|
|
21
|
+
host,
|
|
22
|
+
status,
|
|
23
|
+
renderer,
|
|
24
|
+
camera: new THREE.PerspectiveCamera(),
|
|
25
|
+
provider,
|
|
26
|
+
starField,
|
|
27
|
+
autoResize: false,
|
|
28
|
+
autoDispose: false,
|
|
29
|
+
autoStart: false,
|
|
30
|
+
maxDevicePixelRatio: 1.5,
|
|
31
|
+
plugins: [
|
|
32
|
+
(context) => context.addPart({
|
|
33
|
+
id: 'extra-part',
|
|
34
|
+
attach() { extraPartCalls.push('attach'); },
|
|
35
|
+
start() { extraPartCalls.push('start'); },
|
|
36
|
+
}),
|
|
37
|
+
],
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
assert.equal(host.children[0], renderer.domElement);
|
|
41
|
+
assert.equal(host.style.touchAction, 'none');
|
|
42
|
+
assert.equal(renderer.clearColor.color, 0x02040b);
|
|
43
|
+
assert.deepEqual(renderer.size, { width: 640, height: 360, updateStyle: true });
|
|
44
|
+
assert.equal(renderer.pixelRatio, 1.5);
|
|
45
|
+
assert.equal(provider.sessions.length, 1);
|
|
46
|
+
assert.equal(provider.sessions[0].subscribers.size, 1);
|
|
47
|
+
assert.equal(provider.sessions[0].updateViewCalls.length, 2);
|
|
48
|
+
assert.deepEqual(extraPartCalls, ['attach', 'start']);
|
|
49
|
+
assert.match(status.textContent, /"starsLoaded": 0/);
|
|
50
|
+
|
|
51
|
+
const marker = new THREE.Object3D();
|
|
52
|
+
const markerHandle = browser.addObject(marker, {
|
|
53
|
+
id: 'hyades-marker',
|
|
54
|
+
positionPc: { x: 17.574, y: 42.316, z: 13.963 },
|
|
55
|
+
});
|
|
56
|
+
await Promise.resolve();
|
|
57
|
+
assert.ok(Math.abs(marker.position.x - 0.017574) < 1e-12);
|
|
58
|
+
assert.ok(Math.abs(marker.position.y - 0.042316) < 1e-12);
|
|
59
|
+
assert.ok(Math.abs(marker.position.z - 0.013963) < 1e-12);
|
|
60
|
+
assert.equal(browser.viewer.roots.originContentRoot.children.includes(marker), true);
|
|
61
|
+
markerHandle.remove();
|
|
62
|
+
await Promise.resolve();
|
|
63
|
+
assert.equal(browser.viewer.roots.originContentRoot.children.includes(marker), false);
|
|
64
|
+
|
|
65
|
+
browser.resize();
|
|
66
|
+
assert.equal(fakeWindow.addedEvents.length, 0);
|
|
67
|
+
|
|
68
|
+
await browser.dispose();
|
|
69
|
+
assert.equal(provider.disposed, false, 'caller-owned provider should not be disposed');
|
|
70
|
+
assert.equal(provider.sessions[0].disposed, true);
|
|
71
|
+
assert.equal(starField.disposed, true);
|
|
72
|
+
assert.equal(renderer.disposed, false, 'caller-owned renderer should not be disposed');
|
|
73
|
+
assert.equal(host.children.length, 0);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test('createSkykitBrowser registers resize and page-lifecycle cleanup by default', async () => {
|
|
78
|
+
await withFakeWindow(async (fakeWindow) => {
|
|
79
|
+
const host = createHost();
|
|
80
|
+
const renderer = createRenderer();
|
|
81
|
+
const provider = createProvider();
|
|
82
|
+
const starField = createStarField();
|
|
83
|
+
|
|
84
|
+
const browser = await createSkykitBrowser({
|
|
85
|
+
host,
|
|
86
|
+
status: false,
|
|
87
|
+
renderer,
|
|
88
|
+
provider,
|
|
89
|
+
starField,
|
|
90
|
+
autoStart: false,
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
assert.deepEqual(fakeWindow.addedEvents.map((entry) => entry.type), [
|
|
94
|
+
'resize',
|
|
95
|
+
'pagehide',
|
|
96
|
+
'beforeunload',
|
|
97
|
+
]);
|
|
98
|
+
|
|
99
|
+
await browser.dispose();
|
|
100
|
+
|
|
101
|
+
assert.deepEqual(fakeWindow.removedEvents.map((entry) => entry.type), [
|
|
102
|
+
'resize',
|
|
103
|
+
'pagehide',
|
|
104
|
+
'beforeunload',
|
|
105
|
+
]);
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test('createSkykitBrowser accepts startup lookAt and mouse look mode', async () => {
|
|
110
|
+
await withFakeWindow(async () => {
|
|
111
|
+
const browser = await createSkykitBrowser({
|
|
112
|
+
host: createHost(),
|
|
113
|
+
status: false,
|
|
114
|
+
renderer: createRenderer(),
|
|
115
|
+
provider: createProvider(),
|
|
116
|
+
starField: createStarField(),
|
|
117
|
+
autoResize: false,
|
|
118
|
+
autoDispose: false,
|
|
119
|
+
autoStart: false,
|
|
120
|
+
mouseMode: 'strafe',
|
|
121
|
+
lookAt: { targetPc: { x: 10, y: 0, z: 0 } },
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
const view = browser.viewer.getViewState();
|
|
125
|
+
assert.deepEqual(view.targetPc, { x: 10, y: 0, z: 0 });
|
|
126
|
+
assert.ok(view.orientationIcrs);
|
|
127
|
+
assert.equal(
|
|
128
|
+
browser.viewer.getSnapshot().parts.some((part) => part.id === 'mouse-look'),
|
|
129
|
+
true,
|
|
130
|
+
);
|
|
131
|
+
assert.equal(
|
|
132
|
+
browser.viewer.getSnapshot().parts.some((part) => part.id === 'sky-grab'),
|
|
133
|
+
false,
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
await browser.dispose();
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test('createSkykitBrowser can disable mouse drag controls', async () => {
|
|
141
|
+
await withFakeWindow(async () => {
|
|
142
|
+
const browser = await createSkykitBrowser({
|
|
143
|
+
host: createHost(),
|
|
144
|
+
status: false,
|
|
145
|
+
renderer: createRenderer(),
|
|
146
|
+
provider: createProvider(),
|
|
147
|
+
starField: createStarField(),
|
|
148
|
+
autoResize: false,
|
|
149
|
+
autoDispose: false,
|
|
150
|
+
autoStart: false,
|
|
151
|
+
mouseMode: 'none',
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
assert.equal(
|
|
155
|
+
browser.viewer.getSnapshot().parts.some((part) => part.id === 'mouse-look' || part.id === 'sky-grab'),
|
|
156
|
+
false,
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
await browser.dispose();
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test('browser.install adds plugins after startup and cleans returned teardowns', async () => {
|
|
164
|
+
await withFakeWindow(async () => {
|
|
165
|
+
const calls = [];
|
|
166
|
+
const browser = await createSkykitBrowser({
|
|
167
|
+
host: createHost(),
|
|
168
|
+
status: false,
|
|
169
|
+
renderer: createRenderer(),
|
|
170
|
+
provider: createProvider(),
|
|
171
|
+
starField: createStarField(),
|
|
172
|
+
autoResize: false,
|
|
173
|
+
autoDispose: false,
|
|
174
|
+
autoStart: false,
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
const uninstall = await browser.install((context) => {
|
|
178
|
+
context.addPart({
|
|
179
|
+
id: 'late-part',
|
|
180
|
+
attach() { calls.push('attach'); },
|
|
181
|
+
start() { calls.push('start'); },
|
|
182
|
+
});
|
|
183
|
+
return () => calls.push('teardown');
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
await Promise.resolve();
|
|
187
|
+
assert.deepEqual(calls, ['attach', 'start']);
|
|
188
|
+
assert.equal(browser.viewer.getSnapshot().parts.some((part) => part.id === 'late-part'), true);
|
|
189
|
+
uninstall();
|
|
190
|
+
assert.deepEqual(calls, ['attach', 'start', 'teardown']);
|
|
191
|
+
|
|
192
|
+
await browser.dispose();
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
test('browser journey capability transitions through navigation actions and loads instances', async () => {
|
|
197
|
+
await withFakeWindow(async () => {
|
|
198
|
+
const browser = await createSkykitBrowser({
|
|
199
|
+
host: createHost(),
|
|
200
|
+
status: false,
|
|
201
|
+
renderer: createRenderer(),
|
|
202
|
+
provider: createProvider(),
|
|
203
|
+
starField: createStarField(),
|
|
204
|
+
autoResize: false,
|
|
205
|
+
autoDispose: false,
|
|
206
|
+
autoStart: false,
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
await browser.journey.transitionTo({
|
|
210
|
+
lookAt: { targetPc: { x: 10, y: 0, z: 0 } },
|
|
211
|
+
durationSecs: 1,
|
|
212
|
+
});
|
|
213
|
+
browser.viewer.update(1);
|
|
214
|
+
browser.viewer.update(0);
|
|
215
|
+
assert.ok(browser.viewer.getViewState().orientationIcrs);
|
|
216
|
+
assert.equal(browser.capabilities.has('skykit:navigation'), true);
|
|
217
|
+
|
|
218
|
+
const journey = await browser.journey.load({
|
|
219
|
+
initial: 'home',
|
|
220
|
+
scenes: {
|
|
221
|
+
home: { view: { observerPc: { x: 0, y: 0, z: 0 } } },
|
|
222
|
+
away: { view: { observerPc: { x: 1, y: 2, z: 3 } } },
|
|
223
|
+
},
|
|
224
|
+
});
|
|
225
|
+
await journey.goTo('away');
|
|
226
|
+
browser.viewer.update(0);
|
|
227
|
+
assert.deepEqual(browser.viewer.getViewState().observerPc, { x: 1, y: 2, z: 3 });
|
|
228
|
+
assert.equal(journey.getSnapshot().disposed, false);
|
|
229
|
+
journey.dispose();
|
|
230
|
+
assert.equal(journey.getSnapshot().disposed, true);
|
|
231
|
+
|
|
232
|
+
await browser.dispose();
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
test('browser constellations capability loads manifest boundaries without art', async () => {
|
|
237
|
+
await withFakeWindow(async () => {
|
|
238
|
+
const browser = await createSkykitBrowser({
|
|
239
|
+
host: createHost(),
|
|
240
|
+
status: false,
|
|
241
|
+
renderer: createRenderer(),
|
|
242
|
+
provider: createProvider(),
|
|
243
|
+
starField: createStarField(),
|
|
244
|
+
autoResize: false,
|
|
245
|
+
autoDispose: false,
|
|
246
|
+
autoStart: false,
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
const constellations = await browser.constellations.load({
|
|
250
|
+
art: 'off',
|
|
251
|
+
manifest: {
|
|
252
|
+
id: 'test-skyculture',
|
|
253
|
+
boundaries: {
|
|
254
|
+
edges: ['001:002 M+ 00:00:00 +00:00:00 01:00:00 +00:00:00 AAA BBB'],
|
|
255
|
+
},
|
|
256
|
+
constellations: [],
|
|
257
|
+
},
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
assert.equal(constellations.getSnapshot().lineCount, 1);
|
|
261
|
+
assert.equal(browser.capabilities.has('skykit:browser.constellations'), true);
|
|
262
|
+
assert.equal(browser.viewer.roots.observerContentRoot.children.some((child) => child.name === 'constellation-boundaries'), true);
|
|
263
|
+
assert.equal(constellations.hide(), false);
|
|
264
|
+
assert.equal(constellations.show(), true);
|
|
265
|
+
|
|
266
|
+
await browser.dispose();
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
test('Skykit browser global resolves existing and future browsers and installs add-ons once', async () => {
|
|
271
|
+
await withFakeWindow(async () => {
|
|
272
|
+
const service = installSkykitBrowserGlobal(/** @type {typeof globalThis} */ ({}));
|
|
273
|
+
const host = createHost();
|
|
274
|
+
const browser = await createSkykitBrowser({
|
|
275
|
+
host,
|
|
276
|
+
status: false,
|
|
277
|
+
renderer: createRenderer(),
|
|
278
|
+
provider: createProvider(),
|
|
279
|
+
starField: createStarField(),
|
|
280
|
+
autoResize: false,
|
|
281
|
+
autoDispose: false,
|
|
282
|
+
autoStart: false,
|
|
283
|
+
});
|
|
284
|
+
let installs = 0;
|
|
285
|
+
service.registerBrowserAddon({
|
|
286
|
+
id: 'test-addon',
|
|
287
|
+
install({ browser: installedBrowser }) {
|
|
288
|
+
installs += 1;
|
|
289
|
+
assert.equal(installedBrowser, browser);
|
|
290
|
+
},
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
const unregister = registerBrowserInstance(service, host, browser);
|
|
294
|
+
assert.equal(await service.whenReady(), browser);
|
|
295
|
+
assert.equal(installs, 1);
|
|
296
|
+
service.registerBrowserAddon({
|
|
297
|
+
id: 'test-addon',
|
|
298
|
+
install() {
|
|
299
|
+
installs += 1;
|
|
300
|
+
},
|
|
301
|
+
});
|
|
302
|
+
assert.equal(installs, 1);
|
|
303
|
+
unregister();
|
|
304
|
+
await browser.dispose();
|
|
305
|
+
});
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
async function withFakeWindow(callback) {
|
|
309
|
+
const previousWindow = globalThis.window;
|
|
310
|
+
const fakeWindow = {
|
|
311
|
+
devicePixelRatio: 2,
|
|
312
|
+
addedEvents: [],
|
|
313
|
+
removedEvents: [],
|
|
314
|
+
addEventListener(type, listener, options) {
|
|
315
|
+
this.addedEvents.push({ type, listener, options });
|
|
316
|
+
},
|
|
317
|
+
removeEventListener(type, listener) {
|
|
318
|
+
this.removedEvents.push({ type, listener });
|
|
319
|
+
},
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
Object.defineProperty(globalThis, 'window', {
|
|
323
|
+
configurable: true,
|
|
324
|
+
value: fakeWindow,
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
try {
|
|
328
|
+
await callback(fakeWindow);
|
|
329
|
+
} finally {
|
|
330
|
+
if (previousWindow === undefined) {
|
|
331
|
+
delete globalThis.window;
|
|
332
|
+
} else {
|
|
333
|
+
Object.defineProperty(globalThis, 'window', {
|
|
334
|
+
configurable: true,
|
|
335
|
+
value: previousWindow,
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function createHost() {
|
|
342
|
+
return {
|
|
343
|
+
children: [],
|
|
344
|
+
clientWidth: 640,
|
|
345
|
+
clientHeight: 360,
|
|
346
|
+
style: {},
|
|
347
|
+
appendChild(node) {
|
|
348
|
+
this.children.push(node);
|
|
349
|
+
},
|
|
350
|
+
removeChild(node) {
|
|
351
|
+
const index = this.children.indexOf(node);
|
|
352
|
+
if (index >= 0) this.children.splice(index, 1);
|
|
353
|
+
},
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function createRenderer() {
|
|
358
|
+
return {
|
|
359
|
+
domElement: { nodeName: 'CANVAS' },
|
|
360
|
+
clearColor: null,
|
|
361
|
+
size: null,
|
|
362
|
+
pixelRatio: null,
|
|
363
|
+
disposed: false,
|
|
364
|
+
setClearColor(color, alpha) {
|
|
365
|
+
this.clearColor = { color, alpha };
|
|
366
|
+
},
|
|
367
|
+
setSize(width, height, updateStyle) {
|
|
368
|
+
this.size = { width, height, updateStyle };
|
|
369
|
+
},
|
|
370
|
+
setPixelRatio(value) {
|
|
371
|
+
this.pixelRatio = value;
|
|
372
|
+
},
|
|
373
|
+
render() {},
|
|
374
|
+
dispose() {
|
|
375
|
+
this.disposed = true;
|
|
376
|
+
},
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function createProvider() {
|
|
381
|
+
return {
|
|
382
|
+
sessions: [],
|
|
383
|
+
disposed: false,
|
|
384
|
+
createSession(options) {
|
|
385
|
+
const session = createSession(options);
|
|
386
|
+
this.sessions.push(session);
|
|
387
|
+
return session;
|
|
388
|
+
},
|
|
389
|
+
dispose() {
|
|
390
|
+
this.disposed = true;
|
|
391
|
+
},
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function createSession(options) {
|
|
396
|
+
return {
|
|
397
|
+
id: 'test-session',
|
|
398
|
+
options,
|
|
399
|
+
subscribers: new Set(),
|
|
400
|
+
updateViewCalls: [],
|
|
401
|
+
disposed: false,
|
|
402
|
+
subscribe(callback) {
|
|
403
|
+
this.subscribers.add(callback);
|
|
404
|
+
return () => {
|
|
405
|
+
this.subscribers.delete(callback);
|
|
406
|
+
};
|
|
407
|
+
},
|
|
408
|
+
updateView(view, options) {
|
|
409
|
+
this.updateViewCalls.push({ view, options });
|
|
410
|
+
},
|
|
411
|
+
getSnapshot() {
|
|
412
|
+
return {
|
|
413
|
+
id: this.id,
|
|
414
|
+
updateViewCalls: this.updateViewCalls.length,
|
|
415
|
+
};
|
|
416
|
+
},
|
|
417
|
+
async dispose() {
|
|
418
|
+
this.disposed = true;
|
|
419
|
+
},
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
function createStarField() {
|
|
424
|
+
return {
|
|
425
|
+
object3d: new THREE.Group(),
|
|
426
|
+
disposed: false,
|
|
427
|
+
view: null,
|
|
428
|
+
apply() {},
|
|
429
|
+
setView(view) {
|
|
430
|
+
this.view = view;
|
|
431
|
+
},
|
|
432
|
+
getSnapshot() {
|
|
433
|
+
return {
|
|
434
|
+
starCount: 0,
|
|
435
|
+
hasView: Boolean(this.view),
|
|
436
|
+
};
|
|
437
|
+
},
|
|
438
|
+
dispose() {
|
|
439
|
+
this.disposed = true;
|
|
440
|
+
},
|
|
441
|
+
};
|
|
442
|
+
}
|