@lightningjs/blits 1.6.1 → 1.7.1

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,6 +1,19 @@
1
1
  # Changelog
2
2
 
3
- ## v1.6.0
3
+ ## v1.7.1
4
+
5
+ _14 oct 2024_
6
+
7
+ - Fixed support for `.otf` and `.woff` fonts files in combination with MSDF generator
8
+
9
+
10
+ ## v1.7.0
11
+
12
+ _14 oct 2024_
13
+
14
+ - Added cleanup upon component destroy of effects generated by global reactivity
15
+
16
+ ## v1.6.0 / v1.6.1
4
17
 
5
18
  _9 oct 2024_
6
19
 
package/bin/index.js CHANGED
File without changes
@@ -125,9 +125,9 @@ Blits comes with a default keycode mapping. This mapping is a sensible default t
125
125
 
126
126
  But it's possible that the keycodes and mapping of your target device are slightly or even completely different.
127
127
 
128
- In Blits, you can easily configure the key mapping to match your needs. In the `src/index.js` file where we instantiate the App via the `Blits.Launch` function, we can add an extra key, called `keys`, to the _settings object_.
128
+ In Blits, you can easily configure the key mapping to match your needs. In the `src/index.js` file where we instantiate the App via the `Blits.Launch` function, we can add an extra key, called `keymap`, to the _settings object_.
129
129
 
130
- The `keys` item should be an object literal, where you map a `key` or `keyCode` (from the `KeyboardEvent`) to an event name that you can use in your Components.
130
+ The `keymap` should contain an object literal, where you map a `key` or `keyCode` (from the `KeyboardEvent`) to an event name that you can use in your Components.
131
131
 
132
132
  > You can use a site like [keyjs.dev](https://keyjs.dev/) to find the appropriate key and keyCode for your device
133
133
 
@@ -137,7 +137,7 @@ Blits.Launch(App, 'app', {
137
137
  w: 1920,
138
138
  h: 1080,
139
139
  //...
140
- keys: {
140
+ keymap: {
141
141
  // switch left and right using the key
142
142
  ArrowLeft: 'right',
143
143
  ArrowRight: 'left',
@@ -152,7 +152,7 @@ Blits.Launch(App, 'app', {
152
152
  })
153
153
  ```
154
154
 
155
- The custom keys object will be merged with the default key mapping, that looks like this:
155
+ The custom keymap object will be merged with the default key mapping, that looks like this:
156
156
 
157
157
  ```js
158
158
  const defaultKeyMap = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightningjs/blits",
3
- "version": "1.6.1",
3
+ "version": "1.7.1",
4
4
  "description": "Blits: The Lightning 3 App Development Framework",
5
5
  "bin": "bin/index.js",
6
6
  "exports": {
@@ -20,6 +20,7 @@ import Focus from '../../focus.js'
20
20
  import eventListeners from '../../lib/eventListeners.js'
21
21
  import { trigger } from '../../lib/reactivity/effect.js'
22
22
  import { Log } from '../../lib/log.js'
23
+ import { removeGlobalEffects } from '../../lib/reactivity/effect.js'
23
24
 
24
25
  export default {
25
26
  focus: {
@@ -56,6 +57,7 @@ export default {
56
57
  this.$clearIntervals()
57
58
  eventListeners.removeListeners(this)
58
59
  deleteChildren(this[symbols.children])
60
+ removeGlobalEffects(this[symbols.effects])
59
61
  Log.debug(`Destroyed component ${this.componentId}`)
60
62
  },
61
63
  writable: false,
@@ -32,6 +32,8 @@ let counter = 0
32
32
  export default function (component, config) {
33
33
  component[symbols.identifier] = ++counter
34
34
 
35
+ component[symbols.effects] = []
36
+
35
37
  // setup hooks
36
38
  registerHooks(config.hooks, component[symbols.identifier])
37
39
 
package/src/component.js CHANGED
@@ -161,9 +161,12 @@ const Component = (name = required('name'), config = required('config')) => {
161
161
  // reactive bindings define in the template
162
162
  const effects = config.code.effects
163
163
  for (let i = 0; i < effects.length; i++) {
164
- effect(() => {
164
+ const eff = () => {
165
165
  effects[i](this, this[symbols.children], config, globalComponents, rootComponent, effect)
166
- })
166
+ }
167
+ // store reference to the effect
168
+ this[symbols.effects].push(eff)
169
+ effect(eff)
167
170
  }
168
171
 
169
172
  // setup watchers if the components has watchers specified
@@ -392,13 +392,15 @@ const generateForLoopCode = function (templateObject, parent) {
392
392
  }
393
393
 
394
394
  // inner scope variables are part of the main forloop
395
- innerScopeEffects.forEach((effect) => {
395
+ innerScopeEffects.forEach((effect, index) => {
396
396
  const key = effect.indexOf(`scope.${index}`) > -1 ? `'${interpolate(result[2], '')}'` : null
397
397
  if (effect.indexOf("Symbol.for('props')") === -1) {
398
398
  ctx.renderCode.push(`
399
- effect(() => {
399
+ let eff${index} = () => {
400
400
  ${effect}
401
- }, ${key})
401
+ }
402
+ effect(eff${index}, ${key})
403
+ component[Symbol.for('effects')].push(eff${index})
402
404
  `)
403
405
  } else {
404
406
  // props shouldn't be wrapped in an effect, but simply passed on
@@ -29,8 +29,22 @@ export const resumeTracking = () => {
29
29
  }
30
30
 
31
31
  const objectMap = new WeakMap()
32
+ const globalEffectsMap = new Map()
32
33
 
33
- export const track = (target, key) => {
34
+ export const removeGlobalEffects = (effectsToRemove) => {
35
+ if (globalEffectsMap.size === 0) return
36
+ for (const [effect, target] of globalEffectsMap) {
37
+ if (effectsToRemove.indexOf(effect) === -1) continue
38
+ const effectsSet = objectMap.get(target)
39
+ if (effectsSet === undefined) continue
40
+ for (const set of effectsSet.values()) {
41
+ set.delete(effect)
42
+ globalEffectsMap.delete(effect)
43
+ }
44
+ }
45
+ }
46
+
47
+ export const track = (target, key, global = false) => {
34
48
  if (currentEffect) {
35
49
  if (paused) {
36
50
  return
@@ -49,6 +63,8 @@ export const track = (target, key) => {
49
63
  effectsMap.set(key, effects)
50
64
  }
51
65
  effects.add(currentEffect)
66
+
67
+ if (global === true) globalEffectsMap.set(currentEffect, target)
52
68
  }
53
69
  }
54
70
 
@@ -28,7 +28,7 @@ export const getRaw = (value) => {
28
28
  return raw ? getRaw(raw) : value
29
29
  }
30
30
 
31
- const reactiveProxy = (original, _parent = null, _key) => {
31
+ const reactiveProxy = (original, _parent = null, _key, global) => {
32
32
  // don't create a proxy when a Blits component or an Image Texture
33
33
  // is assigned to a state variable
34
34
  if (typeof original === 'object') {
@@ -53,7 +53,7 @@ const reactiveProxy = (original, _parent = null, _key) => {
53
53
  if (Array.isArray(target)) {
54
54
  if (typeof target[key] === 'object' && target[key] !== null) {
55
55
  if (Array.isArray(target[key])) {
56
- track(target, key)
56
+ track(target, key, global)
57
57
  }
58
58
  // create a new reactive proxy
59
59
  return reactiveProxy(getRaw(target[key]), target, key)
@@ -81,7 +81,7 @@ const reactiveProxy = (original, _parent = null, _key) => {
81
81
  // handling objects (but not null values, which have object type in JS)
82
82
  if (typeof target[key] === 'object' && target[key] !== null) {
83
83
  if (Array.isArray(target[key])) {
84
- track(target, key)
84
+ track(target, key, global)
85
85
  }
86
86
  // create a new reactive proxy
87
87
  return reactiveProxy(getRaw(target[key]), target, key)
@@ -89,7 +89,7 @@ const reactiveProxy = (original, _parent = null, _key) => {
89
89
 
90
90
  // handling all other types
91
91
  // track the key on the target
92
- track(target, key)
92
+ track(target, key, global)
93
93
  // return the reflected value
94
94
  return Reflect.get(target, key, receiver)
95
95
  },
@@ -119,7 +119,7 @@ const reactiveProxy = (original, _parent = null, _key) => {
119
119
  return proxy
120
120
  }
121
121
 
122
- const reactiveDefineProperty = (target) => {
122
+ const reactiveDefineProperty = (target, global) => {
123
123
  Object.keys(target).forEach((key) => {
124
124
  let internalValue = target[key]
125
125
 
@@ -140,7 +140,7 @@ const reactiveDefineProperty = (target) => {
140
140
  enumerable: true,
141
141
  configurable: true,
142
142
  get() {
143
- track(target, key)
143
+ track(target, key, global)
144
144
  return internalValue
145
145
  },
146
146
  set(newValue) {
@@ -157,8 +157,10 @@ const reactiveDefineProperty = (target) => {
157
157
  return target
158
158
  }
159
159
 
160
- export const reactive = (target, mode = 'Proxy') => {
161
- return mode === 'defineProperty' ? reactiveDefineProperty(target) : reactiveProxy(target)
160
+ export const reactive = (target, mode = 'Proxy', global = false) => {
161
+ return mode === 'defineProperty'
162
+ ? reactiveDefineProperty(target, global)
163
+ : reactiveProxy(target, undefined, undefined, global)
162
164
  }
163
165
 
164
166
  export const memo = (raw) => {
@@ -46,4 +46,6 @@ export default {
46
46
  componentType: Symbol.for('componentType'),
47
47
  // Symbol 'isComponent' utilized within generated code
48
48
  isComponent: Symbol.for('isComponent'),
49
+ // Symbol 'effects' utilized within generated code
50
+ effects: Symbol.for('effects'),
49
51
  }
@@ -21,6 +21,6 @@ import Settings from '../settings.js'
21
21
  export default {
22
22
  name: 'appState',
23
23
  plugin(state = {}) {
24
- return reactive(state, Settings.get('reactivityMode'))
24
+ return reactive(state, Settings.get('reactivityMode'), true)
25
25
  },
26
26
  }
@@ -32,7 +32,8 @@ export default {
32
32
  language: '',
33
33
  loaded: 0,
34
34
  },
35
- Settings.get('reactivityMode')
35
+ Settings.get('reactivityMode'),
36
+ true
36
37
  )
37
38
 
38
39
  const setLanguage = (language) => {
@@ -52,7 +52,8 @@ export default {
52
52
  {
53
53
  current: 'default',
54
54
  },
55
- Settings.get('reactivityMode')
55
+ Settings.get('reactivityMode'),
56
+ true
56
57
  )
57
58
 
58
59
  let themes = {}
@@ -49,9 +49,19 @@ export default function () {
49
49
  if (!fs.existsSync(path.resolve(fontDir, file))) {
50
50
  const fontName = match[1]
51
51
  const ext = match[2]
52
- const fontFile = path.join(fontDir, `${fontName}.ttf`)
53
52
 
54
- if (fs.existsSync(fontFile)) {
53
+ // Attempt to find the font file with supported extensions
54
+ const supportedExtensions = ['ttf', 'otf', 'woff']
55
+ let fontFile = null
56
+ for (const fontExt of supportedExtensions) {
57
+ const potentialPath = path.join(fontDir, `${fontName}.${fontExt}`)
58
+ if (fs.existsSync(potentialPath)) {
59
+ fontFile = potentialPath
60
+ break
61
+ }
62
+ }
63
+
64
+ if (fontFile) {
55
65
  const generatedFontFile = path.join(
56
66
  msdfOutputDir,
57
67
  fontPath,
@@ -83,7 +93,7 @@ export default function () {
83
93
  next() // Handle case where generation might have failed
84
94
  }
85
95
  } else {
86
- next() // ttf file does not exist
96
+ next() // No supported font file found
87
97
  }
88
98
  } else {
89
99
  next() // file exists
@@ -95,18 +105,18 @@ export default function () {
95
105
  },
96
106
  // after build ends, the first hook is renderStart where we can modify the output
97
107
  async renderStart() {
98
- const ttfFiles = findAllTtfFiles(publicDir)
108
+ const fontFiles = findAllFontFiles(publicDir)
99
109
 
100
- for (const ttfFile of ttfFiles) {
101
- const relativePath = path.relative(publicDir, path.dirname(ttfFile))
102
- const baseName = path.basename(ttfFile).replace(/\.ttf$/i, '')
110
+ for (const fontFile of fontFiles) {
111
+ const relativePath = path.relative(publicDir, path.dirname(fontFile))
112
+ const baseName = path.basename(fontFile).replace(/\.(ttf|otf|woff)$/i, '')
103
113
  const msdfJsonPath = path.join(msdfOutputDir, relativePath, `${baseName}.msdf.json`)
104
114
  const msdfPngPath = path.join(msdfOutputDir, relativePath, `${baseName}.msdf.png`)
105
115
 
106
116
  // Check if MSDF files are generated, if not, generate them
107
117
  if (!fs.existsSync(msdfJsonPath) || !fs.existsSync(msdfPngPath)) {
108
- console.log(`Generating missing MSDF files for ${ttfFile}`)
109
- await generateSDF(ttfFile, path.join(msdfOutputDir, relativePath))
118
+ console.log(`Generating missing MSDF files for ${fontFile}`)
119
+ await generateSDF(fontFile, path.join(msdfOutputDir, relativePath))
110
120
  }
111
121
  }
112
122
 
@@ -129,17 +139,17 @@ const generateSDF = async (inputFilePath, outputDirPath) => {
129
139
  else console.error('Failed to generate MSDF file')
130
140
  }
131
141
 
132
- // finds all TTF files in a directory
133
- const findAllTtfFiles = (dir, filesList = []) => {
142
+ // Finds all font files (.ttf, .otf, .woff) in a directory
143
+ const findAllFontFiles = (dir, filesList = []) => {
134
144
  const files = fs.readdirSync(dir, { withFileTypes: true })
135
145
 
136
146
  files.forEach((file) => {
137
147
  if (file.isDirectory()) {
138
148
  const dirPath = path.join(dir, file.name)
139
- findAllTtfFiles(dirPath, filesList)
149
+ findAllFontFiles(dirPath, filesList)
140
150
  } else {
141
- // Check for all case variations of TTF extension
142
- if (file.name.match(/\.ttf$/i)) {
151
+ // Check for supported font extensions
152
+ if (file.name.match(/\.(ttf|otf|woff)$/i)) {
143
153
  filesList.push(path.join(dir, file.name))
144
154
  }
145
155
  }