@lightningjs/blits 1.7.0 → 1.8.0
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 +18 -0
- package/docs/components/user_input.md +4 -4
- package/docs/essentials/displaying_text.md +53 -1
- package/docs/transitions_animations/transitions.md +21 -1
- package/index.d.ts +819 -14
- package/package.json +4 -3
- package/src/engines/L3/element.js +9 -1
- package/vite/msdfGenerator.js +114 -25
- package/src/application.d.ts +0 -174
- package/src/component.d.ts +0 -455
- package/src/launch.d.ts +0 -273
- package/src/plugin.d.ts +0 -71
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lightningjs/blits",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
4
4
|
"description": "Blits: The Lightning 3 App Development Framework",
|
|
5
5
|
"bin": "bin/index.js",
|
|
6
6
|
"exports": {
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"prepublishOnly": "node scripts/prepublishOnly.js",
|
|
20
20
|
"postpublish": "node scripts/postpublish.js"
|
|
21
21
|
},
|
|
22
|
+
"types": "./index.d.ts",
|
|
22
23
|
"lint-staged": {
|
|
23
24
|
"*.js": [
|
|
24
25
|
"eslint --fix"
|
|
@@ -47,8 +48,8 @@
|
|
|
47
48
|
"tape": "^5.5.0"
|
|
48
49
|
},
|
|
49
50
|
"dependencies": {
|
|
50
|
-
"@lightningjs/msdf-generator": "^1.0
|
|
51
|
-
"@lightningjs/renderer": "^2.
|
|
51
|
+
"@lightningjs/msdf-generator": "^1.1.0",
|
|
52
|
+
"@lightningjs/renderer": "^2.4.0"
|
|
52
53
|
},
|
|
53
54
|
"repository": {
|
|
54
55
|
"type": "git",
|
|
@@ -386,13 +386,21 @@ const Element = {
|
|
|
386
386
|
f,
|
|
387
387
|
}
|
|
388
388
|
|
|
389
|
-
if (transition.start && typeof transition.start === 'function') {
|
|
389
|
+
if (transition.start !== undefined && typeof transition.start === 'function') {
|
|
390
390
|
// fire transition start callback when animation really starts (depending on specified delay)
|
|
391
391
|
f.once('animating', () => {
|
|
392
392
|
transition.start.call(this.component, this, prop, startValue)
|
|
393
393
|
})
|
|
394
394
|
}
|
|
395
395
|
|
|
396
|
+
if (transition.progress !== undefined && typeof transition.progress === 'function') {
|
|
397
|
+
let prevProgress = 0
|
|
398
|
+
f.on('tick', (_node, { progress }) => {
|
|
399
|
+
transition.progress.call(this.component, this, prop, progress, prevProgress)
|
|
400
|
+
prevProgress = progress
|
|
401
|
+
})
|
|
402
|
+
}
|
|
403
|
+
|
|
396
404
|
f.once('stopped', () => {
|
|
397
405
|
// remove the prop from scheduled transitions
|
|
398
406
|
this.scheduledTransitions[prop] = undefined
|
package/vite/msdfGenerator.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import path from 'path'
|
|
2
2
|
import * as fs from 'fs'
|
|
3
|
+
import { createHash } from 'crypto'
|
|
3
4
|
import { genFont, setGeneratePaths } from '@lightningjs/msdf-generator'
|
|
4
5
|
import { adjustFont } from '@lightningjs/msdf-generator/adjustFont'
|
|
5
6
|
|
|
@@ -21,6 +22,8 @@ class TaskQueue {
|
|
|
21
22
|
|
|
22
23
|
const fontGenerationQueue = new TaskQueue()
|
|
23
24
|
let config
|
|
25
|
+
let checksumPaths = []
|
|
26
|
+
let checksumData = []
|
|
24
27
|
|
|
25
28
|
export default function () {
|
|
26
29
|
let msdfOutputDir = ''
|
|
@@ -49,9 +52,21 @@ export default function () {
|
|
|
49
52
|
if (!fs.existsSync(path.resolve(fontDir, file))) {
|
|
50
53
|
const fontName = match[1]
|
|
51
54
|
const ext = match[2]
|
|
52
|
-
const fontFile = path.join(fontDir, `${fontName}.ttf`)
|
|
53
55
|
|
|
54
|
-
|
|
56
|
+
// Attempt to find the font file with supported extensions
|
|
57
|
+
const supportedExtensions = ['ttf', 'otf', 'woff']
|
|
58
|
+
let fontFile = null
|
|
59
|
+
let fontType = null
|
|
60
|
+
for (const fontExt of supportedExtensions) {
|
|
61
|
+
const potentialPath = path.join(fontDir, `${fontName}.${fontExt}`)
|
|
62
|
+
if (fs.existsSync(potentialPath)) {
|
|
63
|
+
fontFile = potentialPath
|
|
64
|
+
fontType = '.' + fontExt
|
|
65
|
+
break
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (fontFile) {
|
|
55
70
|
const generatedFontFile = path.join(
|
|
56
71
|
msdfOutputDir,
|
|
57
72
|
fontPath,
|
|
@@ -60,10 +75,13 @@ export default function () {
|
|
|
60
75
|
const mimeType = ext === 'png' ? 'image/png' : 'application/json'
|
|
61
76
|
|
|
62
77
|
await fontGenerationQueue.enqueue(async () => {
|
|
63
|
-
if (
|
|
64
|
-
|
|
78
|
+
if (isGenerationRequired(fontDir, targetDir, fontName, fontType)) {
|
|
79
|
+
const configFilePath = path.join(fontDir, `${fontName}.config.json`)
|
|
80
|
+
|
|
65
81
|
console.log(`\nGenerating ${targetDir}/${fontName}.msdf.${ext}`)
|
|
66
|
-
await generateSDF(fontFile, path.join(targetDir))
|
|
82
|
+
await generateSDF(fontFile, path.join(targetDir), configFilePath)
|
|
83
|
+
|
|
84
|
+
saveChecksum()
|
|
67
85
|
}
|
|
68
86
|
})
|
|
69
87
|
|
|
@@ -83,7 +101,7 @@ export default function () {
|
|
|
83
101
|
next() // Handle case where generation might have failed
|
|
84
102
|
}
|
|
85
103
|
} else {
|
|
86
|
-
next() //
|
|
104
|
+
next() // No supported font file found
|
|
87
105
|
}
|
|
88
106
|
} else {
|
|
89
107
|
next() // file exists
|
|
@@ -95,18 +113,21 @@ export default function () {
|
|
|
95
113
|
},
|
|
96
114
|
// after build ends, the first hook is renderStart where we can modify the output
|
|
97
115
|
async renderStart() {
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
for (const
|
|
101
|
-
const relativePath = path.relative(publicDir, path.dirname(
|
|
102
|
-
const
|
|
103
|
-
const
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
// Check
|
|
107
|
-
if (
|
|
108
|
-
|
|
109
|
-
|
|
116
|
+
const fontFiles = findAllFontFiles(publicDir)
|
|
117
|
+
|
|
118
|
+
for (const fontFile of fontFiles) {
|
|
119
|
+
const relativePath = path.relative(publicDir, path.dirname(fontFile))
|
|
120
|
+
const fontExt = path.extname(fontFile)
|
|
121
|
+
const baseName = path.basename(fontFile).replace(/\.(ttf|otf|woff)$/i, '')
|
|
122
|
+
const targetDir = path.join(msdfOutputDir, relativePath)
|
|
123
|
+
|
|
124
|
+
// Check MSDF generation is required
|
|
125
|
+
if (isGenerationRequired(path.dirname(fontFile), targetDir, baseName, fontExt)) {
|
|
126
|
+
const configFilePath = path.join(path.dirname(fontFile), `${baseName}.config.json`)
|
|
127
|
+
console.log(`Generating missing MSDF files for ${fontFile}`)
|
|
128
|
+
await generateSDF(fontFile, path.join(msdfOutputDir, relativePath), configFilePath)
|
|
129
|
+
|
|
130
|
+
saveChecksum()
|
|
110
131
|
}
|
|
111
132
|
}
|
|
112
133
|
|
|
@@ -117,11 +138,11 @@ export default function () {
|
|
|
117
138
|
}
|
|
118
139
|
}
|
|
119
140
|
|
|
120
|
-
const generateSDF = async (inputFilePath, outputDirPath) => {
|
|
141
|
+
const generateSDF = async (inputFilePath, outputDirPath, configFilePath) => {
|
|
121
142
|
// Ensure the destination directory exists
|
|
122
143
|
fs.mkdirSync(outputDirPath, { recursive: true })
|
|
123
144
|
|
|
124
|
-
setGeneratePaths(path.dirname(inputFilePath), outputDirPath)
|
|
145
|
+
setGeneratePaths(path.dirname(inputFilePath), outputDirPath, configFilePath)
|
|
125
146
|
|
|
126
147
|
let font = await genFont(path.basename(inputFilePath), 'msdf')
|
|
127
148
|
|
|
@@ -129,17 +150,17 @@ const generateSDF = async (inputFilePath, outputDirPath) => {
|
|
|
129
150
|
else console.error('Failed to generate MSDF file')
|
|
130
151
|
}
|
|
131
152
|
|
|
132
|
-
//
|
|
133
|
-
const
|
|
153
|
+
// Finds all font files (.ttf, .otf, .woff) in a directory
|
|
154
|
+
const findAllFontFiles = (dir, filesList = []) => {
|
|
134
155
|
const files = fs.readdirSync(dir, { withFileTypes: true })
|
|
135
156
|
|
|
136
157
|
files.forEach((file) => {
|
|
137
158
|
if (file.isDirectory()) {
|
|
138
159
|
const dirPath = path.join(dir, file.name)
|
|
139
|
-
|
|
160
|
+
findAllFontFiles(dirPath, filesList)
|
|
140
161
|
} else {
|
|
141
|
-
// Check for
|
|
142
|
-
if (file.name.match(/\.ttf$/i)) {
|
|
162
|
+
// Check for supported font extensions
|
|
163
|
+
if (file.name.match(/\.(ttf|otf|woff)$/i)) {
|
|
143
164
|
filesList.push(path.join(dir, file.name))
|
|
144
165
|
}
|
|
145
166
|
}
|
|
@@ -158,6 +179,74 @@ const copyDir = (src, dest) => {
|
|
|
158
179
|
const srcPath = path.join(src, entry.name)
|
|
159
180
|
const destPath = path.join(dest, entry.name)
|
|
160
181
|
|
|
182
|
+
// Should not copy .checksum files to dist directory
|
|
183
|
+
if (srcPath.indexOf('.checksum') !== -1) continue
|
|
184
|
+
|
|
161
185
|
entry.isDirectory() ? copyDir(srcPath, destPath) : fs.copyFileSync(srcPath, destPath) // Copy files
|
|
162
186
|
}
|
|
163
187
|
}
|
|
188
|
+
|
|
189
|
+
// Check font config.json or font file modified since last generation
|
|
190
|
+
const isGenerationRequired = (fontDir, targetDir, fontName, fontExt) => {
|
|
191
|
+
const fontFilePath = path.resolve(fontDir, fontName + fontExt)
|
|
192
|
+
const fontChecksumPath = path.resolve(targetDir, fontName + fontExt + '.checksum')
|
|
193
|
+
|
|
194
|
+
const configJsonPath = path.resolve(fontDir, fontName + '.config.json')
|
|
195
|
+
const configChecksumPath = path.resolve(targetDir, fontName + '.config.checksum')
|
|
196
|
+
|
|
197
|
+
// If font file checksum not exists, should generate msdf
|
|
198
|
+
if (!fs.existsSync(fontChecksumPath)) {
|
|
199
|
+
checksumPaths.push(fontChecksumPath)
|
|
200
|
+
checksumData.push(generateHash(fontFilePath))
|
|
201
|
+
|
|
202
|
+
if (fs.existsSync(configJsonPath)) {
|
|
203
|
+
checksumPaths.push(configChecksumPath)
|
|
204
|
+
checksumData.push(generateHash(configJsonPath))
|
|
205
|
+
}
|
|
206
|
+
return true
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// If config.json exists, can be newly added or modified since last generation of msdf
|
|
210
|
+
if (fs.existsSync(configJsonPath)) {
|
|
211
|
+
// If config.json checksum not exists, should generate msdf
|
|
212
|
+
if (!fs.existsSync(configChecksumPath)) {
|
|
213
|
+
checksumPaths.push(configChecksumPath)
|
|
214
|
+
checksumData.push(generateHash(configJsonPath))
|
|
215
|
+
return true
|
|
216
|
+
}
|
|
217
|
+
const isConfigModified = isInputFileModified(configJsonPath, configChecksumPath)
|
|
218
|
+
|
|
219
|
+
// If config.json is modified, should generate msdf
|
|
220
|
+
if (isConfigModified) return true
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Check font file modified, if modified, should generate msdf
|
|
224
|
+
return isInputFileModified(fontFilePath, fontChecksumPath)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const isInputFileModified = (inputFilePath, targetFilePath) => {
|
|
228
|
+
const inputChecksum = generateHash(inputFilePath)
|
|
229
|
+
const targetChecksum = fs.readFileSync(targetFilePath).toString()
|
|
230
|
+
if (inputChecksum !== targetChecksum) {
|
|
231
|
+
checksumPaths.push(targetFilePath)
|
|
232
|
+
checksumData.push(inputChecksum)
|
|
233
|
+
return true
|
|
234
|
+
}
|
|
235
|
+
return false
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const generateHash = (filePath) => {
|
|
239
|
+
const buffer = fs.readFileSync(filePath)
|
|
240
|
+
return createHash('md5').update(buffer).digest('hex')
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const saveChecksum = () => {
|
|
244
|
+
if (checksumPaths.length > 0 && checksumData.length > 0) {
|
|
245
|
+
for (let i = 0; i < checksumPaths.length; i++) {
|
|
246
|
+
// Save checksum
|
|
247
|
+
fs.writeFileSync(checksumPaths[i], checksumData[i])
|
|
248
|
+
}
|
|
249
|
+
checksumData.length = 0
|
|
250
|
+
checksumPaths.length = 0
|
|
251
|
+
}
|
|
252
|
+
}
|
package/src/application.d.ts
DELETED
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
/*
|
|
3
|
-
* Copyright 2023 Comcast Cable Communications Management, LLC
|
|
4
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
-
* you may not use this file except in compliance with the License.
|
|
6
|
-
* You may obtain a copy of the License at
|
|
7
|
-
*
|
|
8
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
-
*
|
|
10
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
-
* See the License for the specific language governing permissions and
|
|
14
|
-
* limitations under the License.
|
|
15
|
-
*
|
|
16
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
import {default as Component} from './component'
|
|
20
|
-
|
|
21
|
-
declare namespace Application {
|
|
22
|
-
|
|
23
|
-
interface Transition {
|
|
24
|
-
/**
|
|
25
|
-
* Name of the prop to transition (i.e. 'x', 'y', 'alpha', 'color')
|
|
26
|
-
*/
|
|
27
|
-
prop: string,
|
|
28
|
-
/**
|
|
29
|
-
* Value the prop should transition to (i.e. 0, 100, '#223388')
|
|
30
|
-
*/
|
|
31
|
-
value: any,
|
|
32
|
-
/**
|
|
33
|
-
* Duration of the transition in milliseconds, defaults to 300
|
|
34
|
-
*/
|
|
35
|
-
duration?: number,
|
|
36
|
-
/**
|
|
37
|
-
* Easing function to apply to the transition
|
|
38
|
-
*/
|
|
39
|
-
easing?: string,
|
|
40
|
-
/**
|
|
41
|
-
* Delay before the transition starts in milliseconds
|
|
42
|
-
*/
|
|
43
|
-
delay?: number
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
interface Before {
|
|
47
|
-
/**
|
|
48
|
-
* Name of the prop to set before the transition starts
|
|
49
|
-
*/
|
|
50
|
-
prop: string,
|
|
51
|
-
/**
|
|
52
|
-
* Value the prop to set before the transition starts
|
|
53
|
-
*/
|
|
54
|
-
value: any,
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
interface RouteTransition {
|
|
58
|
-
/**
|
|
59
|
-
* Setting or Array of Settings before new view enters into the router view
|
|
60
|
-
*/
|
|
61
|
-
before: Before | Before[],
|
|
62
|
-
/**
|
|
63
|
-
* Transition or Array of Transitions for new view to enters into the router view
|
|
64
|
-
*/
|
|
65
|
-
in: Transition | Transition[],
|
|
66
|
-
/**
|
|
67
|
-
* Transition or Array of Transitions for old view to leave the router view
|
|
68
|
-
*/
|
|
69
|
-
out: Transition | Transition[],
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
type RouteTransitionFunction = (previousRoute: Route, currentRoute: Route) => RequireAtLeastOne<RouteTransition>
|
|
73
|
-
|
|
74
|
-
interface RouteAnnounce {
|
|
75
|
-
/**
|
|
76
|
-
* Message to be announced
|
|
77
|
-
*/
|
|
78
|
-
message: String,
|
|
79
|
-
/**
|
|
80
|
-
* Politeness level
|
|
81
|
-
*
|
|
82
|
-
* Defaults to 'off'
|
|
83
|
-
*/
|
|
84
|
-
politeness?: 'off' | 'polite' | 'assertive'
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
type RequireAtLeastOne<T> = {
|
|
88
|
-
[K in keyof T]-?: Required<Pick<T, K>> & Partial<Pick<T, Exclude<keyof T, K>>>
|
|
89
|
-
}[keyof T]
|
|
90
|
-
|
|
91
|
-
interface RouteHooks {
|
|
92
|
-
before?: (to: Route, from: Route) => string | Route;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
type Route = {
|
|
96
|
-
/**
|
|
97
|
-
* URI path for the route
|
|
98
|
-
*/
|
|
99
|
-
path: string,
|
|
100
|
-
/**
|
|
101
|
-
* Component to load when activating the route
|
|
102
|
-
*/
|
|
103
|
-
component: Component,
|
|
104
|
-
/**
|
|
105
|
-
* Transition configuration for the route
|
|
106
|
-
*/
|
|
107
|
-
transition?: RequireAtLeastOne<RouteTransition> | RouteTransitionFunction,
|
|
108
|
-
/**
|
|
109
|
-
* Extra route options
|
|
110
|
-
*/
|
|
111
|
-
options?: object // todo: specify which options are available,
|
|
112
|
-
/**
|
|
113
|
-
* Message to be announced when visiting the route (often used for accessibility purposes)
|
|
114
|
-
*
|
|
115
|
-
* Can be either a `String` with the message or an object that defines the message and the
|
|
116
|
-
* politeness level
|
|
117
|
-
*/
|
|
118
|
-
announce?: String | RouteAnnounce
|
|
119
|
-
/**
|
|
120
|
-
* Register hooks for the route
|
|
121
|
-
*/
|
|
122
|
-
hooks?: RouteHooks
|
|
123
|
-
/**
|
|
124
|
-
* Route path parameters
|
|
125
|
-
*/
|
|
126
|
-
readonly params?: {
|
|
127
|
-
[key: string]: string | number
|
|
128
|
-
}
|
|
129
|
-
/**
|
|
130
|
-
* Allows for attaching custom data to a route, either hardcoded in the
|
|
131
|
-
* route definition or asigned to the route object in a before hook
|
|
132
|
-
*
|
|
133
|
-
* Will be merged with the route params and navigation data and passed as
|
|
134
|
-
* props into the route component
|
|
135
|
-
*/
|
|
136
|
-
data?: {
|
|
137
|
-
[key: string]: any
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
export interface ApplicationInstance extends Component.ComponentInstance {}
|
|
142
|
-
|
|
143
|
-
export interface ApplicationConfig<Props extends string, S, M> extends Component.ComponentConfig<Props, S, M> {
|
|
144
|
-
/**
|
|
145
|
-
* Routes definition
|
|
146
|
-
*
|
|
147
|
-
* @example
|
|
148
|
-
*
|
|
149
|
-
* ```js
|
|
150
|
-
* routes: [
|
|
151
|
-
* { path: '/', component: Home },
|
|
152
|
-
* { path: '/details', component: Details },
|
|
153
|
-
* { path: '/account', component: Account },
|
|
154
|
-
* ]
|
|
155
|
-
* ```
|
|
156
|
-
*/
|
|
157
|
-
routes?: Route[]
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Blits.Application()
|
|
163
|
-
*
|
|
164
|
-
* Root App component
|
|
165
|
-
*/
|
|
166
|
-
declare function Application<
|
|
167
|
-
Props extends string,
|
|
168
|
-
S extends Component.State,
|
|
169
|
-
M extends Component.Methods
|
|
170
|
-
>(
|
|
171
|
-
config: Application.ApplicationConfig<Props, S, M>
|
|
172
|
-
) : Application.ApplicationInstance
|
|
173
|
-
|
|
174
|
-
export default Application;
|