@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.
Files changed (77) hide show
  1. package/bands/bands.js +3 -1
  2. package/bundle/bundle.js +56 -0
  3. package/list/list.js +12 -1
  4. package/lottie/lottie.layers.js +64 -11
  5. package/lottie/lottie.paint.js +30 -0
  6. package/lottie/lottie.paths.js +19 -4
  7. package/lottie/lottie.props.js +18 -8
  8. package/lottie/lottie.shapes.js +2 -1
  9. package/mixer/mixer.js +31 -8
  10. package/mixer/mixer.mix.js +17 -0
  11. package/motion/motion.convert.js +32 -1
  12. package/motion/motion.flow.js +20 -2
  13. package/motion/motion.machine.js +28 -13
  14. package/motion/motion.values.js +13 -9
  15. package/motion.js +196 -20
  16. package/package.json +10 -16
  17. package/player/player.js +25 -5
  18. package/readme.md +78 -3
  19. package/script/luau/lib/base/base.js +202 -0
  20. package/script/luau/lib/base/base.table.js +189 -0
  21. package/script/luau/lib/lib.args.js +152 -0
  22. package/script/luau/lib/lib.js +23 -0
  23. package/script/luau/lib/math/math.bit32.js +144 -0
  24. package/script/luau/lib/math/math.js +288 -0
  25. package/script/luau/lib/math/math.vector.js +125 -0
  26. package/script/luau/lib/string/string.format.js +143 -0
  27. package/script/luau/lib/string/string.js +160 -0
  28. package/script/luau/lib/string/string.pattern.js +505 -0
  29. package/script/luau/load/load.js +362 -0
  30. package/script/luau/load/load.ops.js +79 -0
  31. package/script/luau/luau.convert.js +259 -0
  32. package/script/luau/luau.js +132 -0
  33. package/script/luau/luau.md +111 -0
  34. package/script/luau/vm/vm.js +167 -0
  35. package/script/luau/vm/vm.meta.js +401 -0
  36. package/script/luau/vm/vm.num.js +236 -0
  37. package/script/luau/vm/vm.run.js +842 -0
  38. package/script/luau/vm/vm.value.js +406 -0
  39. package/script/play/play.js +199 -0
  40. package/script/play/play.objects.js +295 -0
  41. package/script/play/play.scope.js +91 -0
  42. package/script/rive/api/api.statics.js +31 -0
  43. package/script/rive/api/api.type.js +188 -0
  44. package/script/rive/api/artboard/artboard.js +89 -0
  45. package/script/rive/api/assets/assets.js +72 -0
  46. package/script/rive/api/data/data.value.js +99 -0
  47. package/script/rive/api/draw/draw.contours.js +163 -0
  48. package/script/rive/api/draw/draw.measure.js +196 -0
  49. package/script/rive/api/draw/draw.paint.js +235 -0
  50. package/script/rive/api/draw/draw.path.js +153 -0
  51. package/script/rive/api/draw/draw.renderer.js +117 -0
  52. package/script/rive/api/math/math.color.js +96 -0
  53. package/script/rive/api/math/math.mat2d.js +180 -0
  54. package/script/rive/api/math/math.vector.js +121 -0
  55. package/script/rive/api/media/media.audio.js +113 -0
  56. package/script/rive/api/media/media.draw.js +73 -0
  57. package/script/rive/api/media/media.image.js +61 -0
  58. package/script/rive/api/media/media.mesh.js +50 -0
  59. package/script/rive/api/pointer/pointer.event.js +64 -0
  60. package/script/rive/api/pointer/pointer.route.js +69 -0
  61. package/script/rive/api/text/text.font.js +129 -0
  62. package/script/rive/api/text/text.js +278 -0
  63. package/script/rive/api/text/text.layout.js +259 -0
  64. package/script/rive/context/context.js +76 -0
  65. package/script/rive/context/context.memory.js +196 -0
  66. package/script/rive/context/context.viewmodel.js +302 -0
  67. package/script/rive/context/lists/lists.js +160 -0
  68. package/script/rive/context/lists/lists.values.js +281 -0
  69. package/script/rive/host/host.globals.js +44 -0
  70. package/script/rive/host/host.js +533 -0
  71. package/script/rive/host/host.node.js +45 -0
  72. package/script/rive/rive.js +23 -0
  73. package/script/rive/rive.md +180 -0
  74. package/script/script.js +11 -0
  75. package/skin/skin.deform.js +514 -0
  76. package/skin/skin.ik.js +260 -0
  77. 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
+ }