@lightningjs/blits 2.0.0-beta.1 → 2.0.0-beta.10

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.
Files changed (57) hide show
  1. package/CHANGELOG.md +83 -0
  2. package/README.md +2 -0
  3. package/element-test-full.txt +0 -0
  4. package/index.d.ts +114 -14
  5. package/package.json +28 -8
  6. package/scripts/lint.js +17 -0
  7. package/scripts/prepublishOnly.js +15 -6
  8. package/scripts/prepush.js +25 -7
  9. package/src/announcer/announcer.js +54 -6
  10. package/src/announcer/speechSynthesis.js +156 -37
  11. package/src/application.js +98 -3
  12. package/src/component/base/methods.js +5 -2
  13. package/src/component/base/router.js +6 -0
  14. package/src/component/base/timeouts_intervals.js +56 -0
  15. package/src/component/setup/routes.js +5 -0
  16. package/src/component/setup/state.js +2 -0
  17. package/src/component.js +64 -24
  18. package/src/components/Circle.js +5 -3
  19. package/src/components/FPScounter.js +1591 -1577
  20. package/src/components/RouterView.js +141 -124
  21. package/src/components/Sprite.js +65 -8
  22. package/src/components/index.js +0 -2
  23. package/src/constants.js +17 -0
  24. package/src/engine.js +17 -0
  25. package/src/engines/L3/element.js +101 -7
  26. package/src/engines/L3/fontLoader.js +17 -0
  27. package/src/engines/L3/index.js +17 -0
  28. package/src/engines/L3/launch.js +4 -1
  29. package/src/engines/L3/shaderLoader.js +17 -0
  30. package/src/engines/index.js +17 -0
  31. package/src/{focus.js → focus/focus.js} +6 -18
  32. package/src/focus/helpers.js +31 -0
  33. package/src/focus/hover.js +94 -0
  34. package/src/helpers/fetchJson.js +4 -6
  35. package/src/launch.js +1 -0
  36. package/src/lib/codegenerator/generator.js +55 -9
  37. package/src/lib/lifecycle.js +34 -3
  38. package/src/lib/log.js +4 -4
  39. package/src/lib/precompiler/precompiler.js +23 -14
  40. package/src/lib/reactivity/effect.js +3 -2
  41. package/src/lib/reactivityguard/computedprops.js +113 -28
  42. package/src/lib/shaders/shaders.js +2 -1
  43. package/src/lib/symbols.js +19 -0
  44. package/src/lib/utils.js +17 -0
  45. package/src/plugin.js +17 -0
  46. package/src/plugins/appstate.d.ts +26 -0
  47. package/src/plugins/index.d.ts +28 -0
  48. package/src/plugins/language.d.ts +37 -0
  49. package/src/plugins/storage/localCookie.js +4 -6
  50. package/src/plugins/storage/storage.d.ts +31 -0
  51. package/src/plugins/theme.d.ts +43 -0
  52. package/src/plugins/theme.js +30 -3
  53. package/src/router/router.js +78 -14
  54. package/test-output.txt +0 -0
  55. package/vite/inject-dev-config.js +17 -0
  56. package/vite/preCompiler.js +15 -5
  57. package/vscode/data/template-attributes.json +335 -86
@@ -21,7 +21,7 @@ import { reactive } from '../lib/reactivity/reactive.js'
21
21
  import symbols from '../lib/symbols.js'
22
22
  import { Log } from '../lib/log.js'
23
23
  import { stage } from '../launch.js'
24
- import Focus from '../focus.js'
24
+ import Focus from '../focus/focus.js'
25
25
  import Announcer from '../announcer/announcer.js'
26
26
  import Settings from '../settings.js'
27
27
 
@@ -30,6 +30,9 @@ import Settings from '../settings.js'
30
30
  * @typedef {import('../component.js').BlitsComponent} BlitsComponent - The element of the route
31
31
  * @typedef {import('../engines/L3/element.js').BlitsElement} BlitsElement - The element of the route
32
32
  *
33
+ * @typedef {BlitsComponent|BlitsComponentFactory} RouteView
34
+ * @typedef {RouteView & { default?: BlitsComponentFactory }} RouteViewWithOptionalDefault
35
+ *
33
36
  * @typedef {Object} Route
34
37
  * @property {string} path - The path of the route
35
38
  * @property {string} hash - The hash of the route
@@ -57,6 +60,7 @@ export const state = reactive(
57
60
  data: null,
58
61
  params: null,
59
62
  hash: '',
63
+ backNavigation: true,
60
64
  },
61
65
  Settings.get('reactivityMode'),
62
66
  true
@@ -212,12 +216,17 @@ const defaultOptions = {
212
216
  }
213
217
 
214
218
  const makeRouteObject = (route, overrides) => {
219
+ // FIX: exclude keepAlive from the destination route options. Unlike other
220
+ // overrides, keepAlive applies to the route being LEFT, not the destination.
221
+ // It is consumed by removeView() instead.
222
+ const { keepAlive: _keepAlive, ...destOverrides } = overrideOptions // eslint-disable-line no-unused-vars
223
+
215
224
  const cleanRoute = {
216
225
  hash: overrides.hash,
217
226
  path: route.path,
218
227
  component: route.component,
219
228
  transition: 'transition' in route ? route.transition : fadeInFadeOutTransition,
220
- options: { ...defaultOptions, ...route.options, ...overrideOptions },
229
+ options: { ...defaultOptions, ...route.options, ...destOverrides },
221
230
  announce: route.announce || false,
222
231
  hooks: route.hooks || {},
223
232
  data: { ...route.data, ...navigationData, ...overrides.queryParams },
@@ -247,7 +256,7 @@ export const navigate = async function () {
247
256
  let reuse = false
248
257
  if (preventHashChangeNavigation === false && this[symbols.parent][symbols.routes]) {
249
258
  let previousRoute = currentRoute //? Object.assign({}, currentRoute) : undefined
250
- let route = matchHash(getHash(document.location.hash), this[symbols.parent][symbols.routes])
259
+ let route = matchHash(getHash(location.hash), this[symbols.parent][symbols.routes])
251
260
 
252
261
  currentRoute = route
253
262
 
@@ -258,7 +267,11 @@ export const navigate = async function () {
258
267
  const hooks = this[symbols.parent][symbols.routerHooks]
259
268
  if (hooks.beforeEach) {
260
269
  try {
261
- beforeEachResult = await hooks.beforeEach.call(this.parent, route, previousRoute)
270
+ beforeEachResult = await hooks.beforeEach.call(
271
+ this[symbols.parent],
272
+ route,
273
+ previousRoute
274
+ )
262
275
  if (isString(beforeEachResult)) {
263
276
  currentRoute = previousRoute
264
277
  to(beforeEachResult)
@@ -299,7 +312,11 @@ export const navigate = async function () {
299
312
  let beforeHookOutput
300
313
  if (route.hooks.before) {
301
314
  try {
302
- beforeHookOutput = await route.hooks.before.call(this.parent, route, previousRoute)
315
+ beforeHookOutput = await route.hooks.before.call(
316
+ this[symbols.parent],
317
+ route,
318
+ previousRoute
319
+ )
303
320
  if (isString(beforeHookOutput)) {
304
321
  currentRoute = previousRoute
305
322
  to(beforeHookOutput)
@@ -335,10 +352,15 @@ export const navigate = async function () {
335
352
  return
336
353
  }
337
354
  }
355
+
338
356
  // add the previous route (technically still the current route at this point)
339
357
  // into the history stack when inHistory is true and we're not navigating back
358
+ //
359
+ // FIX: use truthy check instead of `!== undefined` because matchHash()
360
+ // can return `false`, which survives `!== undefined` but has no `.options`.
340
361
  if (
341
- previousRoute !== undefined &&
362
+ previousRoute &&
363
+ previousRoute.options &&
342
364
  previousRoute.options.inHistory === true &&
343
365
  navigatingBack === false
344
366
  ) {
@@ -354,6 +376,7 @@ export const navigate = async function () {
354
376
  /** @type {import('../engines/L3/element.js').BlitsElement} */
355
377
  let holder
356
378
 
379
+ /** @type {RouteViewWithOptionalDefault|undefined|null} */
357
380
  let view
358
381
  let focus
359
382
  // when navigating back let's see if we're navigating back to a route that was kept alive
@@ -397,7 +420,7 @@ export const navigate = async function () {
397
420
  // Update router state after announcements and final route resolution,
398
421
  // right before initializing or restoring the view
399
422
  state.path = route.path
400
- state.params = route.params || {}
423
+ state.params = Object.keys(route.params).length === 0 ? null : route.params
401
424
  state.hash = route.hash
402
425
  state.data = null
403
426
  state.data = route.data || {}
@@ -479,12 +502,13 @@ export const navigate = async function () {
479
502
 
480
503
  // apply out out transition on previous view if available, unless
481
504
  // we're reusing the prvious page component
482
- if (previousRoute !== undefined && reuse === false) {
505
+ // FIX: truthy guard — previousRoute can be `false` (see history-push comment above).
506
+ if (previousRoute && reuse === false) {
483
507
  // only animate when there is a previous route
484
508
  shouldAnimate = true
485
509
  const oldView = this[symbols.children].splice(1, 1).pop()
486
510
  if (oldView) {
487
- removeView(previousRoute, oldView, route.transition.out, navigatingBack)
511
+ await removeView(previousRoute, oldView, route.transition.out, navigatingBack)
488
512
  }
489
513
  }
490
514
 
@@ -492,7 +516,7 @@ export const navigate = async function () {
492
516
  if (route.transition.in) {
493
517
  if (Array.isArray(route.transition.in)) {
494
518
  for (let i = 0; i < route.transition.in.length; i++) {
495
- i === route.transition.length - 1
519
+ i === route.transition.in.length - 1
496
520
  ? await setOrAnimate(holder, route.transition.in[i], shouldAnimate)
497
521
  : setOrAnimate(holder, route.transition.in[i], shouldAnimate)
498
522
  }
@@ -500,6 +524,33 @@ export const navigate = async function () {
500
524
  await setOrAnimate(holder, route.transition.in, shouldAnimate)
501
525
  }
502
526
  }
527
+
528
+ if (this[symbols.parent][symbols.routerHooks]) {
529
+ const hooks = this[symbols.parent][symbols.routerHooks]
530
+ if (hooks.afterEach) {
531
+ try {
532
+ await hooks.afterEach.call(
533
+ this[symbols.parent],
534
+ route, // to
535
+ previousRoute // from
536
+ )
537
+ } catch (error) {
538
+ Log.error('Error in "AfterEach" Hook', error)
539
+ }
540
+ }
541
+ }
542
+
543
+ if (route.hooks.after) {
544
+ try {
545
+ await route.hooks.after.call(
546
+ this[symbols.parent],
547
+ route, // to
548
+ previousRoute // from
549
+ )
550
+ } catch (error) {
551
+ Log.error('Error or Rejected Promise in "After" Hook', error)
552
+ }
553
+ }
503
554
  } else {
504
555
  Log.error(`Route ${route.hash} not found`)
505
556
  const routerHooks = this[symbols.parent][symbols.routerHooks]
@@ -509,6 +560,12 @@ export const navigate = async function () {
509
560
  }
510
561
  }
511
562
 
563
+ // Clear module-level variables after removeView has consumed them.
564
+ // Placed here so it executes for all navigation flows, not only when
565
+ // previousRoute exists and reuse is false.
566
+ overrideOptions = {}
567
+ navigationData = {}
568
+
512
569
  // reset navigating indicators
513
570
  navigatingBack = false
514
571
  state.navigating = false
@@ -536,11 +593,18 @@ const removeView = async (route, view, transition, navigatingBack) => {
536
593
  }
537
594
  }
538
595
 
596
+ // Resolve effective keepAlive: runtime override from $router.to() takes precedence
597
+ // over the static route config option
598
+ const keepAlive =
599
+ overrideOptions.keepAlive !== undefined
600
+ ? overrideOptions.keepAlive
601
+ : route.options && route.options.keepAlive
602
+
539
603
  // cache the page when it's as 'keepAlive' instead of destroying
540
604
  if (
541
605
  navigatingBack === false &&
542
606
  route.options &&
543
- route.options.keepAlive === true &&
607
+ keepAlive === true &&
544
608
  route.options.inHistory === true
545
609
  ) {
546
610
  const historyItem = history[history.length - 1]
@@ -555,7 +619,7 @@ const removeView = async (route, view, transition, navigatingBack) => {
555
619
  * 2. Navigating back, and the previous route is configured with "keep alive" set to true.
556
620
  * 3. Navigating back, and the previous route is not configured with "keep alive" set to true.
557
621
  */
558
- if (route.options && (route.options.keepAlive !== true || navigatingBack === true)) {
622
+ if (route.options && (keepAlive !== true || navigatingBack === true)) {
559
623
  view.destroy()
560
624
  view = null
561
625
  }
@@ -585,11 +649,11 @@ const setOrAnimate = (node, transition, shouldAnimate = true) => {
585
649
  })
586
650
  }
587
651
 
588
- export const to = (location, data = {}, options = {}) => {
652
+ export const to = (path, data = {}, options = {}) => {
589
653
  navigationData = data
590
654
  overrideOptions = options
591
655
 
592
- window.location.hash = location
656
+ location.hash = path.replace(/^#/, '')
593
657
  }
594
658
 
595
659
  export const back = function () {
Binary file
@@ -1,3 +1,20 @@
1
+ /*
2
+ * Copyright 2025 Comcast Cable Communications Management, LLC
3
+ * Licensed under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License.
5
+ * You may obtain a copy of the License at
6
+ *
7
+ * http://www.apache.org/licenses/LICENSE-2.0
8
+ *
9
+ * Unless required by applicable law or agreed to in writing, software
10
+ * distributed under the License is distributed on an "AS IS" BASIS,
11
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ * See the License for the specific language governing permissions and
13
+ * limitations under the License.
14
+ *
15
+ * SPDX-License-Identifier: Apache-2.0
16
+ */
17
+
1
18
  export default function injectDevFlag() {
2
19
  return {
3
20
  name: 'inject-dev-config',
@@ -32,12 +32,22 @@ export default function () {
32
32
 
33
33
  // we should only precompile .blits, .js and .ts files
34
34
  if (
35
- fileExtension === '.js' ||
36
- fileExtension === '.ts' ||
37
- fileExtension === '.blits' ||
38
- fileExtension === '.mjs'
35
+ filePath.indexOf('node_modules') === -1 &&
36
+ (fileExtension === '.js' ||
37
+ fileExtension === '.ts' ||
38
+ fileExtension === '.blits' ||
39
+ fileExtension === '.mjs')
39
40
  ) {
40
- return compiler(source, filePath, config.mode)
41
+ const result = compiler(source, filePath, config.mode)
42
+
43
+ if (typeof result === 'object') {
44
+ return {
45
+ code: result.code,
46
+ map: result.map,
47
+ }
48
+ }
49
+ // No transformation needed - return null so Vite knows no changes were made
50
+ return null
41
51
  }
42
52
 
43
53
  // vite expects null if there is no modification