@a2f0/skyline 0.2.59 → 0.2.60
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/docs/package.md +7 -3
- package/lib/skyline-shell.js +25 -1
- package/package.json +1 -1
- package/site/index.html +4 -1
- package/site/skyline-shell.js +25 -1
- package/site/viewer-modules.json +62 -0
package/docs/package.md
CHANGED
|
@@ -255,7 +255,7 @@ the option out.
|
|
|
255
255
|
|
|
256
256
|
### How the viewer's code loads
|
|
257
257
|
|
|
258
|
-
The package's module (`lib/`) is a small loader that a host bundles (under 4 KB). When
|
|
258
|
+
The package's module (`lib/`) is a small loader that a host bundles (under 4.5 KB). When
|
|
259
259
|
it mounts, it imports the viewer's code from the assets (`skyline-viewer.js`) with a
|
|
260
260
|
dynamic `import()` that carries `webpackIgnore`, `turbopackIgnore` and `@vite-ignore`
|
|
261
261
|
comments, so webpack (including Next.js with `--webpack`), Turbopack and Vite leave it
|
|
@@ -263,13 +263,17 @@ to the browser; Rollup, esbuild and Bun leave an import of a variable alone anyw
|
|
|
263
263
|
bundler that tried to resolve it would fail to build: tell it to ignore that import.
|
|
264
264
|
The viewer then loads its stylesheets, the scene's markup from `skyline-3d.html`, its
|
|
265
265
|
stars from `stars.svg`, and the scene's modules and models from the same directory.
|
|
266
|
-
Each module loads once per page, however many viewers mount.
|
|
266
|
+
Each module loads once per page, however many viewers mount. So that those modules
|
|
267
|
+
arrive together rather than one import at a time, the loader also reads
|
|
268
|
+
`viewer-modules.json`, which the build writes, and adds a `modulepreload` link for each
|
|
269
|
+
to the document's head, leaving out the vendored engine when the host shares its own;
|
|
270
|
+
the links leave the head once the viewer is ready, fails, or is destroyed.
|
|
267
271
|
|
|
268
272
|
### Content security policy and other origins
|
|
269
273
|
|
|
270
274
|
The viewer runs in the host's page, under the host's content security policy. A host
|
|
271
275
|
with one must allow the assets' origin (usually `'self'`) in `script-src`,
|
|
272
|
-
`style-src` and `connect-src` (the viewer fetches its markup and
|
|
276
|
+
`style-src` and `connect-src` (the viewer fetches its markup, stars and module list), and inline
|
|
273
277
|
styles in `style-src`, which the viewer sets on its elements. Serve the assets from
|
|
274
278
|
the host's origin when you can; on another origin, every file must allow the page's
|
|
275
279
|
origin through CORS. The viewer's code runs with the host page's privileges wherever
|
package/lib/skyline-shell.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
// The viewer's code and styles, relative to the assets.
|
|
2
2
|
const VIEWER = "skyline-viewer.js";
|
|
3
3
|
const STYLESHEETS = ["study.css", "viewer.css"];
|
|
4
|
+
// The modules the viewer's first frame needs, which the build lists (scripts/build/viewer-modules.ts).
|
|
5
|
+
const MODULES = "viewer-modules.json";
|
|
6
|
+
// The asset directories whose viewers were offered the host's engine: each directory's first
|
|
7
|
+
// scene keeps the engine it took (three-engine.ts), so its later viewers never fetch the
|
|
8
|
+
// vendored one either.
|
|
9
|
+
const sharedEngines = new Set();
|
|
4
10
|
/** The validated, slash-terminated directory the assets are served from. */
|
|
5
11
|
export function assetsBase(document, assetsUrl) {
|
|
6
12
|
const base = new URL(assetsUrl, document.baseURI);
|
|
@@ -20,6 +26,19 @@ export function assetsBase(document, assetsUrl) {
|
|
|
20
26
|
function importViewer(url) {
|
|
21
27
|
return import(/* webpackIgnore: true */ /* @vite-ignore */ /* turbopackIgnore: true */ url);
|
|
22
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* Asks the browser to fetch every module the viewer's first frame needs at once, rather than
|
|
31
|
+
* each only once the module importing it has arrived, a chain several round trips deep. The
|
|
32
|
+
* links go in the document's head, where modulepreload applies, and leave it once the viewer
|
|
33
|
+
* settles or is destroyed. The vendored engine is fetched only when no viewer from the same
|
|
34
|
+
* assets offered the host's Three.js. Preloading only saves time, so a list that fails to load changes nothing else.
|
|
35
|
+
*/
|
|
36
|
+
function preloadModules(document, assets, hostEngine, signal) {
|
|
37
|
+
return fetch(new URL(MODULES, assets), { signal })
|
|
38
|
+
.then((response) => response.json())
|
|
39
|
+
.then(({ modules, engine }) => signal.aborted ? [] : [...(hostEngine || !engine ? [] : [engine]), ...modules].map((file) => document.head.appendChild(Object.assign(document.createElement("link"), { rel: "modulepreload", href: new URL(file, assets).href }))))
|
|
40
|
+
.catch(() => []);
|
|
41
|
+
}
|
|
23
42
|
/** Resolves once `link` has loaded its stylesheet. */
|
|
24
43
|
function loaded(link) {
|
|
25
44
|
return new Promise((resolve, reject) => {
|
|
@@ -79,6 +98,11 @@ export function mountViewer(container, { assetsUrl, title, ...options }) {
|
|
|
79
98
|
const controller = new AbortController();
|
|
80
99
|
let destroyed = false, mounted = false;
|
|
81
100
|
const context = { ...options, theme, assets, signal: controller.signal };
|
|
101
|
+
if (options.three)
|
|
102
|
+
sharedEngines.add(assets.href);
|
|
103
|
+
const preloads = preloadModules(document, assets, sharedEngines.has(assets.href), controller.signal);
|
|
104
|
+
const dropPreloads = () => void preloads.then((links) => links.forEach((link) => link.remove()));
|
|
105
|
+
controller.signal.addEventListener("abort", dropPreloads, { once: true });
|
|
82
106
|
const ready = new Promise((resolve, reject) => {
|
|
83
107
|
Promise.all([importViewer(new URL(VIEWER, assets).href), ...stylesheets.map(loaded)])
|
|
84
108
|
.then(async ([viewer]) => {
|
|
@@ -108,7 +132,7 @@ export function mountViewer(container, { assetsUrl, title, ...options }) {
|
|
|
108
132
|
});
|
|
109
133
|
// The element and the console report a failure, so a host that ignores `ready` sees no
|
|
110
134
|
// unhandled rejection.
|
|
111
|
-
ready.
|
|
135
|
+
ready.then(dropPreloads, dropPreloads);
|
|
112
136
|
return {
|
|
113
137
|
element,
|
|
114
138
|
ready,
|
package/package.json
CHANGED
package/site/index.html
CHANGED
|
@@ -10,8 +10,11 @@
|
|
|
10
10
|
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
|
11
11
|
<title>Chicago Skyline — Living Night</title>
|
|
12
12
|
<!-- viewer-page.js mounts the viewer from skyline-viewer.js, its styles and skyline-3d.html,
|
|
13
|
-
and starts the scene's code; fetch them alongside it
|
|
13
|
+
and starts the scene's code; fetch them alongside it, with the list of the scene's
|
|
14
|
+
modules that the viewer's shell preloads. -->
|
|
15
|
+
<link rel="modulepreload" href="skyline-shell.js" />
|
|
14
16
|
<link rel="modulepreload" href="skyline-viewer.js" />
|
|
17
|
+
<link rel="preload" href="viewer-modules.json" as="fetch" crossorigin="anonymous" />
|
|
15
18
|
<link rel="preload" href="viewer.css" as="style" />
|
|
16
19
|
<link rel="preload" href="study.css" as="style" />
|
|
17
20
|
<style>
|
package/site/skyline-shell.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
// The viewer's code and styles, relative to the assets.
|
|
2
2
|
const VIEWER = "skyline-viewer.js";
|
|
3
3
|
const STYLESHEETS = ["study.css", "viewer.css"];
|
|
4
|
+
// The modules the viewer's first frame needs, which the build lists (scripts/build/viewer-modules.ts).
|
|
5
|
+
const MODULES = "viewer-modules.json";
|
|
6
|
+
// The asset directories whose viewers were offered the host's engine: each directory's first
|
|
7
|
+
// scene keeps the engine it took (three-engine.ts), so its later viewers never fetch the
|
|
8
|
+
// vendored one either.
|
|
9
|
+
const sharedEngines = new Set();
|
|
4
10
|
/** The validated, slash-terminated directory the assets are served from. */
|
|
5
11
|
export function assetsBase(document, assetsUrl) {
|
|
6
12
|
const base = new URL(assetsUrl, document.baseURI);
|
|
@@ -20,6 +26,19 @@ export function assetsBase(document, assetsUrl) {
|
|
|
20
26
|
function importViewer(url) {
|
|
21
27
|
return import(/* webpackIgnore: true */ /* @vite-ignore */ /* turbopackIgnore: true */ url);
|
|
22
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* Asks the browser to fetch every module the viewer's first frame needs at once, rather than
|
|
31
|
+
* each only once the module importing it has arrived, a chain several round trips deep. The
|
|
32
|
+
* links go in the document's head, where modulepreload applies, and leave it once the viewer
|
|
33
|
+
* settles or is destroyed. The vendored engine is fetched only when no viewer from the same
|
|
34
|
+
* assets offered the host's Three.js. Preloading only saves time, so a list that fails to load changes nothing else.
|
|
35
|
+
*/
|
|
36
|
+
function preloadModules(document, assets, hostEngine, signal) {
|
|
37
|
+
return fetch(new URL(MODULES, assets), { signal })
|
|
38
|
+
.then((response) => response.json())
|
|
39
|
+
.then(({ modules, engine }) => signal.aborted ? [] : [...(hostEngine || !engine ? [] : [engine]), ...modules].map((file) => document.head.appendChild(Object.assign(document.createElement("link"), { rel: "modulepreload", href: new URL(file, assets).href }))))
|
|
40
|
+
.catch(() => []);
|
|
41
|
+
}
|
|
23
42
|
/** Resolves once `link` has loaded its stylesheet. */
|
|
24
43
|
function loaded(link) {
|
|
25
44
|
return new Promise((resolve, reject) => {
|
|
@@ -79,6 +98,11 @@ export function mountViewer(container, { assetsUrl, title, ...options }) {
|
|
|
79
98
|
const controller = new AbortController();
|
|
80
99
|
let destroyed = false, mounted = false;
|
|
81
100
|
const context = { ...options, theme, assets, signal: controller.signal };
|
|
101
|
+
if (options.three)
|
|
102
|
+
sharedEngines.add(assets.href);
|
|
103
|
+
const preloads = preloadModules(document, assets, sharedEngines.has(assets.href), controller.signal);
|
|
104
|
+
const dropPreloads = () => void preloads.then((links) => links.forEach((link) => link.remove()));
|
|
105
|
+
controller.signal.addEventListener("abort", dropPreloads, { once: true });
|
|
82
106
|
const ready = new Promise((resolve, reject) => {
|
|
83
107
|
Promise.all([importViewer(new URL(VIEWER, assets).href), ...stylesheets.map(loaded)])
|
|
84
108
|
.then(async ([viewer]) => {
|
|
@@ -108,7 +132,7 @@ export function mountViewer(container, { assetsUrl, title, ...options }) {
|
|
|
108
132
|
});
|
|
109
133
|
// The element and the console report a failure, so a host that ignores `ready` sees no
|
|
110
134
|
// unhandled rejection.
|
|
111
|
-
ready.
|
|
135
|
+
ready.then(dropPreloads, dropPreloads);
|
|
112
136
|
return {
|
|
113
137
|
element,
|
|
114
138
|
ready,
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"modules": [
|
|
3
|
+
"markup.js",
|
|
4
|
+
"three-engine.js",
|
|
5
|
+
"skyline-3d.js",
|
|
6
|
+
"study-viewer.js",
|
|
7
|
+
"vendor/three-r186.js",
|
|
8
|
+
"skyline-scene.js",
|
|
9
|
+
"models/skyline-geography-data.js",
|
|
10
|
+
"models/skyline-geography.js",
|
|
11
|
+
"models/building-kit.js",
|
|
12
|
+
"models/aon-geographic.js",
|
|
13
|
+
"models/aon-tower.js",
|
|
14
|
+
"models/facade-grid.js",
|
|
15
|
+
"models/blue-cross-geographic.js",
|
|
16
|
+
"models/window-illumination.js",
|
|
17
|
+
"models/celebrations.js",
|
|
18
|
+
"models/borg-warner-geographic.js",
|
|
19
|
+
"models/buckingham-geographic.js",
|
|
20
|
+
"models/chicago-athletic-association-geographic.js",
|
|
21
|
+
"models/crain-geographic.js",
|
|
22
|
+
"models/crain-tower.js",
|
|
23
|
+
"models/gage-geographic.js",
|
|
24
|
+
"models/heritage-geographic.js",
|
|
25
|
+
"models/hyatt-west-tower-geographic.js",
|
|
26
|
+
"models/keith-ascher-geographic.js",
|
|
27
|
+
"models/kemper-geographic.js",
|
|
28
|
+
"models/lake-view-geographic.js",
|
|
29
|
+
"models/maclean-center-geographic.js",
|
|
30
|
+
"models/michigan-boulevard-geographic.js",
|
|
31
|
+
"models/michigan-plaza-south-geographic.js",
|
|
32
|
+
"models/millennium-park-plaza-geographic.js",
|
|
33
|
+
"models/monroe-geographic.js",
|
|
34
|
+
"models/north-michigan-180-geographic.js",
|
|
35
|
+
"models/north-wabash-geographic.js",
|
|
36
|
+
"models/on-the-park-geographic.js",
|
|
37
|
+
"models/one-prudential-geographic.js",
|
|
38
|
+
"models/one-prudential-tower.js",
|
|
39
|
+
"models/peoples-gas-geographic.js",
|
|
40
|
+
"models/railway-exchange-geographic.js",
|
|
41
|
+
"models/river-plaza-geographic.js",
|
|
42
|
+
"models/sheraton-grand-geographic.js",
|
|
43
|
+
"models/six-north-michigan-geographic.js",
|
|
44
|
+
"models/swissotel-geographic.js",
|
|
45
|
+
"models/three-illinois-center-geographic.js",
|
|
46
|
+
"models/trump-geographic.js",
|
|
47
|
+
"models/trump-tower.js",
|
|
48
|
+
"models/two-illinois-center-geographic.js",
|
|
49
|
+
"models/two-prudential-geographic.js",
|
|
50
|
+
"models/two-prudential-tower.js",
|
|
51
|
+
"models/university-club-geographic.js",
|
|
52
|
+
"models/willoughby-tower-geographic.js",
|
|
53
|
+
"solar-time.js",
|
|
54
|
+
"time-of-day.js",
|
|
55
|
+
"models/colour-materials.js",
|
|
56
|
+
"models/colour-palette.js",
|
|
57
|
+
"models/colour-reflections.js",
|
|
58
|
+
"models/night-windows.js",
|
|
59
|
+
"models/material-names.js"
|
|
60
|
+
],
|
|
61
|
+
"engine": "vendor/three-r186.bundle.js"
|
|
62
|
+
}
|