@lightningjs/blits 2.8.5 → 2.8.7

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/CHANGELOG.md CHANGED
@@ -1,4 +1,19 @@
1
1
  # Changelog
2
+ ## 2.8.7
3
+
4
+ _07 aug 2026_
5
+
6
+ - Added `clipradius` attribute for rounded-corner clipping
7
+ - Fixed memory leak in delete children method
8
+ - Early cleanup of effects in destroy sequence
9
+ - Added `framecount` to fpsUpdate emit
10
+ - Bumped renderer to 3.1.6
11
+
12
+ ## 2.8.6
13
+
14
+ _30 jul 2026_
15
+
16
+ - Fixed destroying orphaned views when keepAlive is true but route is not in history
2
17
 
3
18
  ## 2.8.5
4
19
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightningjs/blits",
3
- "version": "2.8.5",
3
+ "version": "2.8.7",
4
4
  "description": "Blits: The Lightning 3 App Development Framework",
5
5
  "bin": "bin/index.js",
6
6
  "exports": {
@@ -87,7 +87,7 @@
87
87
  "tape": "^5.5.0"
88
88
  },
89
89
  "dependencies": {
90
- "@lightningjs/renderer": "^3.1.3",
90
+ "@lightningjs/renderer": "^3.1.6",
91
91
  "magic-string": "^0.30.21"
92
92
  },
93
93
  "repository": {
@@ -104,6 +104,8 @@ export default {
104
104
  // pass focus to the parent so we don't get lost in focus limbo
105
105
  if (this.$hasFocus === true) this[symbols.parent].$focus()
106
106
 
107
+ removeEffects(this[symbols.effects])
108
+
107
109
  // @todo - is this really necessary?
108
110
  // This cause an issue with auto sizing of parent (and required an extra eol check there)
109
111
  for (let key in this[symbols.state]) {
@@ -130,7 +132,6 @@ export default {
130
132
 
131
133
  deleteChildren(this[symbols.children])
132
134
  this[symbols.children].length = 0
133
- removeEffects(this[symbols.effects])
134
135
 
135
136
  this[symbols.state] = {}
136
137
 
@@ -239,7 +240,7 @@ export default {
239
240
  */
240
241
  const deleteChildren = function (children) {
241
242
  for (let i = 0; i < children.length; i++) {
242
- if (!children[i]) return
243
+ if (!children[i]) continue
243
244
  // call destroy when method is available on child
244
245
  if (children[i].destroy && typeof children[i].destroy === 'function') {
245
246
  children[i].destroy()
package/src/component.js CHANGED
@@ -338,7 +338,7 @@ const Component = (name = required('name'), config = required('config')) => {
338
338
  // fpsUpdate event
339
339
  if (config.hooks.fpsUpdate) {
340
340
  const cb = (_r, data) => {
341
- emit('fpsUpdate', this[symbols.identifier], this, [data.fps])
341
+ emit('fpsUpdate', this[symbols.identifier], this, [data.fps, data.frameCount])
342
342
  }
343
343
  renderer.on('fpsUpdate', cb)
344
344
  this[symbols.rendererEventListeners].push({ event: 'fpsUpdate', cb })
@@ -50,6 +50,7 @@ export default () =>
50
50
  'shadow',
51
51
  'shader',
52
52
  'clipping',
53
+ 'clipradius',
53
54
  'overflow',
54
55
  'font',
55
56
  'size',
@@ -52,6 +52,7 @@ export const FPScounter = Blits.Component('FPScounter', {
52
52
  'shadow',
53
53
  'shader',
54
54
  'clipping',
55
+ 'clipradius',
55
56
  'overflow',
56
57
  'font',
57
58
  'size',
@@ -62,6 +62,7 @@ export default () =>
62
62
  'shadow',
63
63
  'shader',
64
64
  'clipping',
65
+ 'clipradius',
65
66
  'overflow',
66
67
  'font',
67
68
  'size',
@@ -483,6 +483,9 @@ const propsTransformer = {
483
483
  set clipping(v) {
484
484
  this.props['clipping'] = v
485
485
  },
486
+ set clipradius(v) {
487
+ this.props['clipRadius'] = v
488
+ },
486
489
  set overflow(v) {
487
490
  this.props['clipping'] = !!!v
488
491
  },
@@ -342,7 +342,9 @@ export const navigate = async function () {
342
342
  ? viewState.overrideOptions.keepAlive
343
343
  : this.previousRoute.options && this.previousRoute.options.keepAlive
344
344
 
345
- // cache the page when it's as 'keepAlive' instead of destroying
345
+ // cache the page when it's marked as 'keepAlive' instead of destroying,
346
+ // but only when navigating forward AND the route is in history (so it can be restored on back)
347
+ let cached = false
346
348
  if (
347
349
  viewState.navigatingBack === false &&
348
350
  keepAlive === true &&
@@ -354,6 +356,7 @@ export const navigate = async function () {
354
356
  if (historyItem !== undefined) {
355
357
  historyItem.view = oldView
356
358
  historyItem.focus = viewState.previousFocus
359
+ cached = true
357
360
  }
358
361
  }
359
362
 
@@ -361,8 +364,13 @@ export const navigate = async function () {
361
364
  * 1. Navigating forward, and the previous route is not configured with "keep alive" set to true.
362
365
  * 2. Navigating back, and the previous route is configured with "keep alive" set to true.
363
366
  * 3. Navigating back, and the previous route is not configured with "keep alive" set to true.
367
+ * 4. Navigating forward, keepAlive is true but caching failed (no inHistory or no history
368
+ * item) — destroy to prevent orphaned components leaking in the eventsMap.
364
369
  */
365
- if (this.previousRoute.options && (keepAlive !== true || viewState.navigatingBack === true)) {
370
+ if (
371
+ (this.previousRoute.options && (keepAlive !== true || viewState.navigatingBack === true)) ||
372
+ !cached
373
+ ) {
366
374
  oldView.destroy()
367
375
  oldView = null
368
376
  }
@@ -394,6 +394,20 @@
394
394
  "RouterView"
395
395
  ]
396
396
  },
397
+ "clipradius": {
398
+ "attrType": "regular",
399
+ "types": [
400
+ "number"
401
+ ],
402
+ "defaultValue": 0,
403
+ "reactive": true,
404
+ "description": "Rounds the corners of the clip area when `clipping` is enabled, instead of clipping to a sharp rectangle. Has no effect when `clipping` is `false`. WebGL only.",
405
+ "usedIn": [
406
+ "Element",
407
+ "Text",
408
+ "RouterView"
409
+ ]
410
+ },
397
411
  "overflow": {
398
412
  "attrType": "regular",
399
413
  "types": [