@px-petals/roku.motion-web 1.0.0 → 1.1.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/bands/bands.js +3 -1
- package/bundle/bundle.js +56 -0
- package/list/list.js +12 -1
- package/lottie/lottie.layers.js +64 -11
- package/lottie/lottie.paint.js +30 -0
- package/lottie/lottie.paths.js +19 -4
- package/lottie/lottie.props.js +18 -8
- package/lottie/lottie.shapes.js +2 -1
- package/mixer/mixer.js +31 -8
- package/mixer/mixer.mix.js +17 -0
- package/motion/motion.convert.js +32 -1
- package/motion/motion.flow.js +20 -2
- package/motion/motion.machine.js +28 -13
- package/motion/motion.values.js +13 -9
- package/motion.js +196 -20
- package/package.json +10 -16
- package/player/player.js +25 -5
- package/readme.md +78 -3
- package/script/luau/lib/base/base.js +202 -0
- package/script/luau/lib/base/base.table.js +189 -0
- package/script/luau/lib/lib.args.js +152 -0
- package/script/luau/lib/lib.js +23 -0
- package/script/luau/lib/math/math.bit32.js +144 -0
- package/script/luau/lib/math/math.js +288 -0
- package/script/luau/lib/math/math.vector.js +125 -0
- package/script/luau/lib/string/string.format.js +143 -0
- package/script/luau/lib/string/string.js +160 -0
- package/script/luau/lib/string/string.pattern.js +505 -0
- package/script/luau/load/load.js +362 -0
- package/script/luau/load/load.ops.js +79 -0
- package/script/luau/luau.convert.js +259 -0
- package/script/luau/luau.js +132 -0
- package/script/luau/luau.md +111 -0
- package/script/luau/vm/vm.js +167 -0
- package/script/luau/vm/vm.meta.js +401 -0
- package/script/luau/vm/vm.num.js +236 -0
- package/script/luau/vm/vm.run.js +842 -0
- package/script/luau/vm/vm.value.js +406 -0
- package/script/play/play.js +199 -0
- package/script/play/play.objects.js +295 -0
- package/script/play/play.scope.js +91 -0
- package/script/rive/api/api.statics.js +31 -0
- package/script/rive/api/api.type.js +188 -0
- package/script/rive/api/artboard/artboard.js +89 -0
- package/script/rive/api/assets/assets.js +72 -0
- package/script/rive/api/data/data.value.js +99 -0
- package/script/rive/api/draw/draw.contours.js +163 -0
- package/script/rive/api/draw/draw.measure.js +196 -0
- package/script/rive/api/draw/draw.paint.js +235 -0
- package/script/rive/api/draw/draw.path.js +153 -0
- package/script/rive/api/draw/draw.renderer.js +117 -0
- package/script/rive/api/math/math.color.js +96 -0
- package/script/rive/api/math/math.mat2d.js +180 -0
- package/script/rive/api/math/math.vector.js +121 -0
- package/script/rive/api/media/media.audio.js +113 -0
- package/script/rive/api/media/media.draw.js +73 -0
- package/script/rive/api/media/media.image.js +61 -0
- package/script/rive/api/media/media.mesh.js +50 -0
- package/script/rive/api/pointer/pointer.event.js +64 -0
- package/script/rive/api/pointer/pointer.route.js +69 -0
- package/script/rive/api/text/text.font.js +129 -0
- package/script/rive/api/text/text.js +278 -0
- package/script/rive/api/text/text.layout.js +259 -0
- package/script/rive/context/context.js +76 -0
- package/script/rive/context/context.memory.js +196 -0
- package/script/rive/context/context.viewmodel.js +302 -0
- package/script/rive/context/lists/lists.js +160 -0
- package/script/rive/context/lists/lists.values.js +281 -0
- package/script/rive/host/host.globals.js +44 -0
- package/script/rive/host/host.js +533 -0
- package/script/rive/host/host.node.js +45 -0
- package/script/rive/rive.js +23 -0
- package/script/rive/rive.md +180 -0
- package/script/script.js +11 -0
- package/skin/skin.deform.js +514 -0
- package/skin/skin.ik.js +260 -0
- package/skin/skin.js +297 -0
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A `ViewModelAdapter` over a player's values store (the flat, dotted
|
|
3
|
+
* `values` the Motion player holds), with lists that land as list rows.
|
|
4
|
+
*
|
|
5
|
+
* A list property holds its items (`{ viewModel?, instance?, values? }` or an
|
|
6
|
+
* instance's name) in the store. The adapter gives a script each item as a
|
|
7
|
+
* view model of its own; a script's `push`, `insert`, `swap`, `remove` and
|
|
8
|
+
* the writes inside an item become a new array of items written back through
|
|
9
|
+
* `store.write`, which the player turns into rows (`list/` keeps a row whose
|
|
10
|
+
* item is unchanged and makes a new one for one that differs).
|
|
11
|
+
*
|
|
12
|
+
* ```js
|
|
13
|
+
* const vm = createValuesViewModel({
|
|
14
|
+
* data: bundle.manifest.data,
|
|
15
|
+
* values: () => flow.values,
|
|
16
|
+
* write: (key, value) => motion.setInputs({ [key]: value }),
|
|
17
|
+
* })
|
|
18
|
+
* // after the player's `values` event:
|
|
19
|
+
* vm.refresh()
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { inputValue, propertyOf } from '../../../../motion/motion.values.js'
|
|
24
|
+
|
|
25
|
+
/** @import { ViewModelAdapter } from '../context.viewmodel.js' */
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @typedef {object} ValuesStore
|
|
29
|
+
* @property {any} [data] the bundle's view model (`manifest.data`): property types, enum names
|
|
30
|
+
* @property {() => Record<string, any>} values the live values by dotted path
|
|
31
|
+
* @property {(key: string, value: any) => void} write sets one value (a list as its array of items); the player reacts and the store then reports it
|
|
32
|
+
* @property {(path: string, item: Record<string, any>) => Record<string, any>} [defaults] a list item's values by property name before the item sets its own (`list/list.items.js`'s `itemValues` over the row's data)
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @typedef {object} Item
|
|
37
|
+
* @property {Record<string, any>} rec `{ viewModel?, instance?, values }`
|
|
38
|
+
* @property {string | null} owner the list property it sits in
|
|
39
|
+
* @property {string} source the list property its defaults and kinds come from (kept when it leaves the list)
|
|
40
|
+
* @property {ViewModelAdapter} adapter
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @typedef {ViewModelAdapter & { refresh: () => void }} ValuesViewModel
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @param {ValuesStore} store
|
|
49
|
+
* @returns {ValuesViewModel}
|
|
50
|
+
*/
|
|
51
|
+
export function createValuesViewModel(store) {
|
|
52
|
+
/** @type {Record<string, { fn: (value: any) => void, last: string }[]>} */
|
|
53
|
+
const subscribers = {}
|
|
54
|
+
/** @type {Record<string, Item[]>} items by list property, as last read */
|
|
55
|
+
const known = {}
|
|
56
|
+
/** @type {Item[]} every item made, so an adapter the script hands back finds its record */
|
|
57
|
+
const all = []
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* @param {any} value
|
|
61
|
+
* @returns {string}
|
|
62
|
+
*/
|
|
63
|
+
const sig = (value) => (Array.isArray(value) ? JSON.stringify(value) : String(value))
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* @param {string} path
|
|
67
|
+
* @returns {string | undefined}
|
|
68
|
+
*/
|
|
69
|
+
const kindOf = (path) => {
|
|
70
|
+
const p = propertyOf(store.data, path)
|
|
71
|
+
if (p) return p.type
|
|
72
|
+
const values = store.values()
|
|
73
|
+
const v = values[path]
|
|
74
|
+
if (v !== undefined) return Array.isArray(v) ? 'list' : typeof v === 'number' ? 'number' : typeof v === 'boolean' ? 'boolean' : 'string'
|
|
75
|
+
const prefix = path + '.'
|
|
76
|
+
for (const key in values) if (key.indexOf(prefix) === 0) return 'viewModel'
|
|
77
|
+
return undefined
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @param {string} path
|
|
82
|
+
* @param {any} value
|
|
83
|
+
*/
|
|
84
|
+
const notify = (path, value) => {
|
|
85
|
+
const list = subscribers[path]
|
|
86
|
+
if (!list) return
|
|
87
|
+
const copy = list.slice()
|
|
88
|
+
for (let n = 0; n < copy.length; n++) {
|
|
89
|
+
copy[n].last = sig(store.values()[path])
|
|
90
|
+
copy[n].fn(value)
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Writes the list's items, so the player makes its rows.
|
|
96
|
+
*
|
|
97
|
+
* @param {string} path
|
|
98
|
+
*/
|
|
99
|
+
const commit = (path) => {
|
|
100
|
+
const items = known[path] || []
|
|
101
|
+
/** @type {any[]} */
|
|
102
|
+
const array = []
|
|
103
|
+
for (let n = 0; n < items.length; n++) array.push(items[n].rec)
|
|
104
|
+
store.write(path, array)
|
|
105
|
+
notify(path, array.length)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* An item as a view model of its own.
|
|
110
|
+
*
|
|
111
|
+
* @param {Record<string, any>} rec
|
|
112
|
+
* @param {string | null} owner
|
|
113
|
+
* @param {string} source
|
|
114
|
+
* @returns {Item}
|
|
115
|
+
*/
|
|
116
|
+
const makeItem = (rec, owner, source) => {
|
|
117
|
+
/** @type {Record<string, ((value: any) => void)[]>} */
|
|
118
|
+
const subs = {}
|
|
119
|
+
/** @type {Item} */
|
|
120
|
+
const item = {
|
|
121
|
+
rec,
|
|
122
|
+
owner,
|
|
123
|
+
source,
|
|
124
|
+
adapter: {
|
|
125
|
+
get: (path) => {
|
|
126
|
+
const own = item.rec.values
|
|
127
|
+
if (own && path in own) return own[path]
|
|
128
|
+
const base = store.defaults ? store.defaults(item.source, item.rec) : {}
|
|
129
|
+
return base[path]
|
|
130
|
+
},
|
|
131
|
+
set: (path, value) => {
|
|
132
|
+
const r = item.rec
|
|
133
|
+
r.values = r.values || {}
|
|
134
|
+
if (r.values[path] === value) return
|
|
135
|
+
r.values[path] = value
|
|
136
|
+
if (subs[path]) subs[path].slice().forEach((fn) => fn(value))
|
|
137
|
+
if (item.owner) commit(item.owner)
|
|
138
|
+
},
|
|
139
|
+
kind: (path) => {
|
|
140
|
+
const v = item.adapter.get(path)
|
|
141
|
+
return typeof v === 'number' ? 'number' : typeof v === 'boolean' ? 'boolean' : typeof v === 'string' ? 'string' : undefined
|
|
142
|
+
},
|
|
143
|
+
subscribe: (path, fn) => {
|
|
144
|
+
const list = subs[path] || (subs[path] = [])
|
|
145
|
+
list.push(fn)
|
|
146
|
+
return () => {
|
|
147
|
+
const at = list.indexOf(fn)
|
|
148
|
+
if (at >= 0) list.splice(at, 1)
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
instance: (_path, name) => makeItem({ viewModel: item.rec.viewModel, instance: name === undefined ? item.rec.instance : name }, null, item.source).adapter,
|
|
152
|
+
},
|
|
153
|
+
}
|
|
154
|
+
all.push(item)
|
|
155
|
+
return item
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* The items of a list, in the store's order; an item the script already holds
|
|
160
|
+
* is the same object again.
|
|
161
|
+
*
|
|
162
|
+
* @param {string} path
|
|
163
|
+
* @returns {Item[]}
|
|
164
|
+
*/
|
|
165
|
+
const itemsOf = (path) => {
|
|
166
|
+
const raw = store.values()[path]
|
|
167
|
+
const array = Array.isArray(raw) ? raw : []
|
|
168
|
+
const before = (known[path] || []).slice()
|
|
169
|
+
/** @type {Item[]} */
|
|
170
|
+
const out = []
|
|
171
|
+
for (let n = 0; n < array.length; n++) {
|
|
172
|
+
const el = array[n]
|
|
173
|
+
const rec = typeof el === 'string' ? { instance: el } : el && typeof el === 'object' ? el : {}
|
|
174
|
+
let at = -1
|
|
175
|
+
for (let k = 0; k < before.length && at < 0; k++) if (before[k].rec === rec) at = k
|
|
176
|
+
if (at < 0) {
|
|
177
|
+
const s = sig(rec)
|
|
178
|
+
for (let k = 0; k < before.length && at < 0; k++) if (sig(before[k].rec) === s) at = k
|
|
179
|
+
}
|
|
180
|
+
if (at >= 0) {
|
|
181
|
+
const item = before.splice(at, 1)[0]
|
|
182
|
+
item.rec = rec
|
|
183
|
+
out.push(item)
|
|
184
|
+
} else out.push(makeItem(rec, path, path))
|
|
185
|
+
}
|
|
186
|
+
known[path] = out
|
|
187
|
+
return out
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* @param {ViewModelAdapter} a
|
|
192
|
+
* @returns {Item | undefined}
|
|
193
|
+
*/
|
|
194
|
+
const findByAdapter = (a) => {
|
|
195
|
+
for (let n = 0; n < all.length; n++) if (all[n].adapter === a) return all[n]
|
|
196
|
+
return undefined
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/** @type {ValuesViewModel} */
|
|
200
|
+
const adapter = {
|
|
201
|
+
get(path) {
|
|
202
|
+
const v = store.values()[path]
|
|
203
|
+
const p = propertyOf(store.data, path)
|
|
204
|
+
return p && p.values && typeof v === 'number' ? p.values[v] : v
|
|
205
|
+
},
|
|
206
|
+
set(path, value) {
|
|
207
|
+
const next = inputValue(store.data, path, value)
|
|
208
|
+
if (store.values()[path] === next) return
|
|
209
|
+
store.write(path, next)
|
|
210
|
+
notify(path, value)
|
|
211
|
+
},
|
|
212
|
+
kind: kindOf,
|
|
213
|
+
subscribe(path, fn) {
|
|
214
|
+
const list = subscribers[path] || (subscribers[path] = [])
|
|
215
|
+
const entry = { fn, last: sig(store.values()[path]) }
|
|
216
|
+
list.push(entry)
|
|
217
|
+
return () => {
|
|
218
|
+
const at = list.indexOf(entry)
|
|
219
|
+
if (at >= 0) list.splice(at, 1)
|
|
220
|
+
}
|
|
221
|
+
},
|
|
222
|
+
fire(path) {
|
|
223
|
+
notify(path, true)
|
|
224
|
+
},
|
|
225
|
+
listSize: (path) => itemsOf(path).length,
|
|
226
|
+
listGet: (path, index) => {
|
|
227
|
+
const items = itemsOf(path)
|
|
228
|
+
return items[index] ? items[index].adapter : undefined
|
|
229
|
+
},
|
|
230
|
+
listInsert(path, item, index) {
|
|
231
|
+
const items = itemsOf(path)
|
|
232
|
+
const added = findByAdapter(item)
|
|
233
|
+
if (!added) return
|
|
234
|
+
added.owner = path
|
|
235
|
+
items.splice(index, 0, added)
|
|
236
|
+
commit(path)
|
|
237
|
+
},
|
|
238
|
+
listRemove(path, index) {
|
|
239
|
+
const items = itemsOf(path)
|
|
240
|
+
if (index < 0 || index >= items.length) return undefined
|
|
241
|
+
const out = items.splice(index, 1)[0]
|
|
242
|
+
out.owner = null
|
|
243
|
+
commit(path)
|
|
244
|
+
return out.adapter
|
|
245
|
+
},
|
|
246
|
+
listClear(path) {
|
|
247
|
+
const items = itemsOf(path)
|
|
248
|
+
if (!items.length) return
|
|
249
|
+
for (let n = 0; n < items.length; n++) items[n].owner = null
|
|
250
|
+
items.length = 0
|
|
251
|
+
commit(path)
|
|
252
|
+
},
|
|
253
|
+
listSwap(path, a, b) {
|
|
254
|
+
const items = itemsOf(path)
|
|
255
|
+
const t = items[a]
|
|
256
|
+
items[a] = items[b]
|
|
257
|
+
items[b] = t
|
|
258
|
+
commit(path)
|
|
259
|
+
},
|
|
260
|
+
instance(path, name) {
|
|
261
|
+
if (kindOf(path) !== 'list') return undefined
|
|
262
|
+
const first = itemsOf(path)[0]
|
|
263
|
+
return makeItem({ viewModel: first ? first.rec.viewModel : undefined, instance: name }, null, path).adapter
|
|
264
|
+
},
|
|
265
|
+
/** Tells subscribers about what the player changed since: call it after the player's `values` event */
|
|
266
|
+
refresh() {
|
|
267
|
+
for (const path in subscribers) {
|
|
268
|
+
const now = sig(store.values()[path])
|
|
269
|
+
const list = subscribers[path].slice()
|
|
270
|
+
for (let n = 0; n < list.length; n++) {
|
|
271
|
+
if (list[n].last === now) continue
|
|
272
|
+
list[n].last = now
|
|
273
|
+
const v = store.values()[path]
|
|
274
|
+
list[n].fn(Array.isArray(v) ? v.length : adapter.get(path))
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
},
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return adapter
|
|
281
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The globals a Rive script sees beyond Luau's standard library: `Vector`,
|
|
3
|
+
* `Color`, `Mat2D`, `Path`, `Paint`, `Gradient`, the values of the converter,
|
|
4
|
+
* listener action and layout protocols (`DataValue`, `PointerEvent`, `Vec2D`),
|
|
5
|
+
* and `late` (the placeholder an input's default is written with, which holds
|
|
6
|
+
* nil until the player sets it).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { EMPTY, NativeFunction } from '../../luau/vm/vm.value.js'
|
|
10
|
+
import { installColor } from '../api/math/math.color.js'
|
|
11
|
+
import { installMat2D } from '../api/math/math.mat2d.js'
|
|
12
|
+
import { installVector } from '../api/math/math.vector.js'
|
|
13
|
+
import { installPaint } from '../api/draw/draw.paint.js'
|
|
14
|
+
import { installPath } from '../api/draw/draw.path.js'
|
|
15
|
+
import { installAudio } from '../api/media/media.audio.js'
|
|
16
|
+
import { installImage } from '../api/media/media.image.js'
|
|
17
|
+
import { installMesh } from '../api/media/media.mesh.js'
|
|
18
|
+
import { installPointer } from '../api/pointer/pointer.event.js'
|
|
19
|
+
import { installFont } from '../api/text/text.font.js'
|
|
20
|
+
import { installText } from '../api/text/text.js'
|
|
21
|
+
import { installData } from '../api/data/data.value.js'
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @param {import('../../luau/luau.js').Luau} luau
|
|
25
|
+
* @param {{ audio?: import('../api/media/media.audio.js').AudioEvent[], fallback?: () => any }} [host] where recorded audio goes; the font text uses when a run names none
|
|
26
|
+
*/
|
|
27
|
+
export function installGlobals(luau, host) {
|
|
28
|
+
const vm = luau.vm
|
|
29
|
+
installVector(vm)
|
|
30
|
+
installColor(vm)
|
|
31
|
+
installMat2D(vm)
|
|
32
|
+
installPath(vm)
|
|
33
|
+
installPaint(vm)
|
|
34
|
+
installImage(vm)
|
|
35
|
+
installMesh(vm)
|
|
36
|
+
installAudio(vm, (host && host.audio) || [])
|
|
37
|
+
installFont(vm)
|
|
38
|
+
installText(vm, (host && host.fallback) || (() => ({ name: '', ascent: -0.8, descent: 0.2, capHeight: -0.7, xHeight: -0.5, weight: 400, isItalic: false })))
|
|
39
|
+
installPointer(vm)
|
|
40
|
+
installData(vm)
|
|
41
|
+
vm.globals.set('late', new NativeFunction('late', function () {
|
|
42
|
+
return EMPTY
|
|
43
|
+
}))
|
|
44
|
+
}
|