@linear_non/stellar-kit 3.0.7 → 3.0.9

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.
@@ -24,6 +24,8 @@ export default class ImageLoader {
24
24
 
25
25
  this._results = new Map() // url -> HTMLImageElement
26
26
  this._objectURLs = [] // for revoke
27
+ this._worker = null // shared worker instance
28
+ this._workerURL = null // blob URL for cleanup
27
29
  }
28
30
 
29
31
  async load() {
@@ -61,6 +63,9 @@ export default class ImageLoader {
61
63
 
62
64
  await Promise.all(toLoad.map(url => this._loadOne(url)))
63
65
 
66
+ // Terminate shared worker if created
67
+ this._terminateWorker()
68
+
64
69
  // build ordered result (use cache first, then freshly loaded)
65
70
  this.images = urls.map(u => this.cache?.get(u) || this._results.get(u) || null)
66
71
  this.isLoaded = true
@@ -69,19 +74,34 @@ export default class ImageLoader {
69
74
  }
70
75
 
71
76
  _createInlineWorker() {
77
+ if (this._worker) return this._worker
78
+
72
79
  const code = `
73
80
  self.onmessage = async (e) => {
74
- const { url } = e.data;
81
+ const { url, id } = e.data;
75
82
  try {
76
83
  const res = await fetch(url);
77
84
  const blob = await res.blob();
78
- self.postMessage({ url, blob });
85
+ self.postMessage({ url, id, blob });
79
86
  } catch (err) {
80
- self.postMessage({ url, error: String(err) });
87
+ self.postMessage({ url, id, error: String(err) });
81
88
  }
82
89
  };`
83
90
  const blob = new Blob([code], { type: "application/javascript" })
84
- return new Worker(URL.createObjectURL(blob))
91
+ this._workerURL = URL.createObjectURL(blob)
92
+ this._worker = new Worker(this._workerURL)
93
+ return this._worker
94
+ }
95
+
96
+ _terminateWorker() {
97
+ if (this._worker) {
98
+ this._worker.terminate()
99
+ this._worker = null
100
+ }
101
+ if (this._workerURL) {
102
+ URL.revokeObjectURL(this._workerURL)
103
+ this._workerURL = null
104
+ }
85
105
  }
86
106
 
87
107
  async _loadOne(url) {
@@ -89,20 +109,25 @@ export default class ImageLoader {
89
109
  if (this.useWorker && typeof Worker !== "undefined") {
90
110
  return new Promise(resolve => {
91
111
  const w = this._createInlineWorker()
92
- w.onmessage = async e => {
112
+ const id = url // use url as unique message id
113
+
114
+ const handler = async e => {
115
+ if (e.data.id !== id) return // not our message
116
+ w.removeEventListener("message", handler)
117
+
93
118
  const { blob, error } = e.data
94
119
  if (error || !blob) {
95
120
  this._bumpProgress()
96
- w.terminate()
97
121
  return resolve(null)
98
122
  }
99
123
  const img = await this._blobToImage(blob)
100
124
  this._store(url, img)
101
125
  this._bumpProgress()
102
- w.terminate()
103
126
  resolve(img)
104
127
  }
105
- w.postMessage({ url })
128
+
129
+ w.addEventListener("message", handler)
130
+ w.postMessage({ url, id })
106
131
  })
107
132
  }
108
133
 
@@ -155,6 +180,7 @@ export default class ImageLoader {
155
180
  }
156
181
 
157
182
  destroy() {
183
+ this._terminateWorker()
158
184
  this._objectURLs.forEach(u => URL.revokeObjectURL(u))
159
185
  this._objectURLs.length = 0
160
186
  this.images = []
@@ -123,6 +123,8 @@ export default class PageEngine {
123
123
  this.smooth = smooth
124
124
  }
125
125
 
126
+ this.trackImages()
127
+
126
128
  this.manager = new Manager()
127
129
 
128
130
  if (this.manager && this.components.length) {
@@ -132,11 +134,18 @@ export default class PageEngine {
132
134
  }
133
135
 
134
136
  this.manager.initialize()
135
- this.manager.animateIn?.()
137
+ this.resize()
136
138
 
137
139
  this.on()
138
- this.trackImages()
139
- this.resize()
140
+ }
141
+
142
+ /**
143
+ * Trigger entry animations on all components.
144
+ * Call this from the page/renderer at the appropriate moment
145
+ * (e.g. after loader completes or onEnterCompleted).
146
+ */
147
+ animateIn() {
148
+ this.manager?.animateIn?.()
140
149
  }
141
150
 
142
151
  /**
@@ -166,7 +175,7 @@ export default class PageEngine {
166
175
 
167
176
  // Setup ScrollTrigger scroller proxy
168
177
  ScrollTrigger.scrollerProxy(wrapper, {
169
- scrollTop: (value) => {
178
+ scrollTop: value => {
170
179
  if (value !== undefined) {
171
180
  this.lenis.scrollTo(value, { immediate: true })
172
181
  }
package/events/Emitter.js CHANGED
@@ -33,7 +33,7 @@ class Emitter {
33
33
  }
34
34
  this.on(event, onceCallback, priority)
35
35
  return () => {
36
- this.off(event, cb)
36
+ this.off(event, onceCallback)
37
37
  }
38
38
  }
39
39
 
@@ -53,6 +53,9 @@ const EVENTS = {
53
53
  APP_MOUSEMOVE: "mousemove",
54
54
  APP_MOUSEDOWN: "mousedown",
55
55
  APP_MOUSEUP: "mouseup",
56
+ APP_ROUTE_IN: "route:in",
57
+ APP_ROUTE_OUT: "route:out",
58
+ APP_ROUTE_END: "route:end",
56
59
  APP_SPLITTEXT_READY: "splittext:ready",
57
60
  APP_IMAGES_LOADED: "images:loaded",
58
61
  APP_IMAGES_PROGRESS: "images:progress",
package/events/Mouse.js CHANGED
@@ -15,8 +15,6 @@ export default class Mouse {
15
15
  down: sniffer.isDevice ? "touchstart" : "mousedown",
16
16
  up: sniffer.isDevice ? "touchend" : "mouseup",
17
17
  }
18
-
19
- this.on()
20
18
  }
21
19
 
22
20
  on() {
@@ -54,7 +52,7 @@ export default class Mouse {
54
52
  onUp = e => {
55
53
  const { x, target } = this.getPos(e)
56
54
  this.state.off = x
57
- const isClick = Math.abs(this.state.on - this.state.off) > 10
55
+ const isClick = Math.abs(this.state.on - this.state.off) <= 10
58
56
  emitter.emit(EVENTS.APP_MOUSEUP, { x, target, isClick })
59
57
  }
60
58
  }
@@ -191,6 +191,6 @@ export default class VirtualScroll {
191
191
 
192
192
  destroy = () => {
193
193
  this.unbind()
194
- emitter.off(EVT_ID, () => {}) // Clear all
194
+ emitter.events[EVT_ID] = []
195
195
  }
196
196
  }
package/kitStore.js CHANGED
@@ -40,12 +40,36 @@ export const kitStore = {
40
40
  breakpoints,
41
41
  mouse,
42
42
  flags,
43
- pageContent: null,
44
43
  assets,
44
+
45
+ /** @type {HTMLElement | null} — main content container (#app) */
46
+ pageContent: null,
47
+
48
+ /** @type {HTMLElement | null} — current page/view element ([data-taxi-view]) */
49
+ currentPage: null,
50
+
51
+ /** @type {URL | string | null} — current URL (set by Application) */
52
+ currentURL: null,
53
+
54
+ /** @type {HTMLElement | null} — loader element (set by Application) */
55
+ load: null,
56
+
45
57
  /**
46
58
  * Scroll scroller element (wrapper for Lenis, pageContent for internal scroll).
47
59
  * Used by ScrollTrigger components to get the correct scroller.
48
60
  * @type {HTMLElement | null}
49
61
  */
50
62
  scroller: null,
63
+
64
+ /** @type {import("./events/Raf").default | null} */
65
+ raf: null,
66
+
67
+ /** @type {import("./events/Scroll").default | null} */
68
+ scroll: null,
69
+
70
+ /** @type {import("./events/Resize").default | null} */
71
+ resize: null,
72
+
73
+ /** @type {any | null} — Taxi router instance (if attached via Application.attachTaxi()) */
74
+ taxi: null,
51
75
  }
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@linear_non/stellar-kit",
3
- "version": "3.0.7",
3
+ "version": "3.0.9",
4
4
  "description": "Stellar frontend core for Non-Linear Studio projects.",
5
5
  "type": "module",
6
- "main": "/core/index.js",
6
+ "main": "./core/index.js",
7
7
  "sass": "./styles/index.scss",
8
8
  "exports": {
9
9
  ".": "./kitStore.js",
package/utils/math.js CHANGED
@@ -31,7 +31,7 @@ export const clamp = (val, min, max) => Math.min(Math.max(val, min), max)
31
31
  * @param {number} [p=3] - Decimal precision
32
32
  * @returns {number}
33
33
  */
34
- export const round = (n, p) => {
35
- const precision = p !== undefined ? Math.pow(10, p) : 1000
34
+ export const round = (n, p = 3) => {
35
+ const precision = Math.pow(10, p)
36
36
  return Math.round(n * precision) / precision
37
37
  }
package/utils/sniffer.js CHANGED
@@ -75,7 +75,7 @@ export const sniffer = {
75
75
  },
76
76
 
77
77
  get isWindows() {
78
- return ["Win32", "Win64", "Windows", "WinCE"].includes(window.navigator.platform)
78
+ return /windows/i.test(this.uA)
79
79
  },
80
80
 
81
81
  get isChrome() {
@@ -89,7 +89,7 @@ export const sniffer = {
89
89
  },
90
90
 
91
91
  get isMac() {
92
- return navigator.platform.toLowerCase().includes("mac")
92
+ return /macintosh|mac os x/i.test(this.uA)
93
93
  },
94
94
 
95
95
  get isDesktop() {
package/utils/support.js CHANGED
@@ -14,7 +14,7 @@ export const supportMouseTouch = () => {
14
14
  hasTouch:
15
15
  "ontouchstart" in window ||
16
16
  window.TouchEvent ||
17
- (window.DocumentTouch && document instanceof DocumentTouch),
17
+ (navigator.maxTouchPoints && navigator.maxTouchPoints > 0),
18
18
  hasTouchWin: navigator.msMaxTouchPoints && navigator.msMaxTouchPoints > 1,
19
19
  hasPointer: !!window.navigator.msPointerEnabled,
20
20
  hasKeyDown: "onkeydown" in document,