@lumiastream/ui 0.3.2 → 0.3.4

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/dist/se-import.js CHANGED
@@ -8,7 +8,7 @@ var SE_TO_LUMIA_PLACEHOLDER = {
8
8
  displayName: "displayname",
9
9
  sender: "gifter",
10
10
  amount: "amount",
11
- currency: "currency",
11
+ currency: "currencySymbol",
12
12
  message: "message",
13
13
  items: "itemsDetailed",
14
14
  item: "firstItem.name",
@@ -23,7 +23,10 @@ var SE_TO_LUMIA_PLACEHOLDER = {
23
23
  streamer: "streamer"
24
24
  };
25
25
  var TWITCH_CONTEXTS = {
26
- "follower-latest": { name: "twitch_last_follower", username: "twitch_last_follower" },
26
+ "follower-latest": {
27
+ name: "twitch_last_follower",
28
+ username: "twitch_last_follower"
29
+ },
27
30
  // Three SE listeners with genuinely distinct semantics — preserve the
28
31
  // distinction in the variable mapping so a widget configured for "Latest
29
32
  // new subscriber" doesn't render the latest *resub* (and vice versa).
@@ -31,9 +34,18 @@ var TWITCH_CONTEXTS = {
31
34
  // are subtype-specific. The dedicated TWITCH_LAST_{NEW_SUBSCRIBER,
32
35
  // RESUBSCRIBER} variables are written by the Twitch event pipeline on
33
36
  // the matching sub.type event.
34
- "subscriber-latest": { name: "twitch_last_subscriber", username: "twitch_last_subscriber" },
35
- "subscriber-new-latest": { name: "twitch_last_new_subscriber", username: "twitch_last_new_subscriber" },
36
- "subscriber-resub-latest": { name: "twitch_last_resubscriber", username: "twitch_last_resubscriber" },
37
+ "subscriber-latest": {
38
+ name: "twitch_last_subscriber",
39
+ username: "twitch_last_subscriber"
40
+ },
41
+ "subscriber-new-latest": {
42
+ name: "twitch_last_new_subscriber",
43
+ username: "twitch_last_new_subscriber"
44
+ },
45
+ "subscriber-resub-latest": {
46
+ name: "twitch_last_resubscriber",
47
+ username: "twitch_last_resubscriber"
48
+ },
37
49
  // `subscriber-gifted-latest`: SE uses `{sender}` for the giver and `{name}` for
38
50
  // the recipient (e.g. "{sender} gifted to {name}"). The two roles must point
39
51
  // at distinct Lumia variables — collapsing both onto `twitch_last_gifter`
@@ -41,12 +53,45 @@ var TWITCH_CONTEXTS = {
41
53
  // nowhere. `sender` keeps the gifter; `name` / `username` switch to the
42
54
  // dedicated recipient variable `twitch_last_gifted`. Amount stays on the
43
55
  // gifter (it's the count of subs in that drop, owned by the gifter).
44
- "subscriber-gifted-latest": { name: "twitch_last_gifted", username: "twitch_last_gifted", sender: "twitch_last_gifter", amount: "twitch_last_gifter_amount", count: "twitch_last_gifter_amount" },
45
- "subscriber-alltime-gifter": { name: "twitch_alltime_top_gifter", username: "twitch_alltime_top_gifter", amount: "twitch_alltime_top_gifter_amount", count: "twitch_alltime_top_gifter_amount" },
46
- "cheer-latest": { name: "twitch_last_bit", username: "twitch_last_bit", amount: "twitch_last_bit_amount", count: "twitch_last_bit_amount" },
47
- "tip-latest": { name: "latest_donator", username: "latest_donator", amount: "latest_donator_amount", count: "latest_donator_amount", currency: "latest_donator_currency_symbol" },
48
- latest_donator: { name: "latest_donator", username: "latest_donator", amount: "latest_donator_amount", count: "latest_donator_amount", currency: "latest_donator_currency_symbol" },
49
- "raid-latest": { name: "twitch_last_raider", username: "twitch_last_raider", amount: "twitch_last_raid_amount", count: "twitch_last_raid_amount" },
56
+ "subscriber-gifted-latest": {
57
+ name: "twitch_last_gifted",
58
+ username: "twitch_last_gifted",
59
+ sender: "twitch_last_gifter",
60
+ amount: "twitch_last_gifter_amount",
61
+ count: "twitch_last_gifter_amount"
62
+ },
63
+ "subscriber-alltime-gifter": {
64
+ name: "twitch_alltime_top_gifter",
65
+ username: "twitch_alltime_top_gifter",
66
+ amount: "twitch_alltime_top_gifter_amount",
67
+ count: "twitch_alltime_top_gifter_amount"
68
+ },
69
+ "cheer-latest": {
70
+ name: "twitch_last_bit",
71
+ username: "twitch_last_bit",
72
+ amount: "twitch_last_bit_amount",
73
+ count: "twitch_last_bit_amount"
74
+ },
75
+ "tip-latest": {
76
+ name: "latest_donator",
77
+ username: "latest_donator",
78
+ amount: "latest_donator_amount",
79
+ count: "latest_donator_amount",
80
+ currency: "latest_donator_currency_symbol"
81
+ },
82
+ latest_donator: {
83
+ name: "latest_donator",
84
+ username: "latest_donator",
85
+ amount: "latest_donator_amount",
86
+ count: "latest_donator_amount",
87
+ currency: "latest_donator_currency_symbol"
88
+ },
89
+ "raid-latest": {
90
+ name: "twitch_last_raider",
91
+ username: "twitch_last_raider",
92
+ amount: "twitch_last_raid_amount",
93
+ count: "twitch_last_raid_amount"
94
+ },
50
95
  // Period counters: count/amount = the running counter for that window.
51
96
  // SE lets the streamer pick session / week / month / total on the same widget
52
97
  // type; the chosen period lands in `widget.listener` (e.g. `cheer-week`).
@@ -54,25 +99,86 @@ var TWITCH_CONTEXTS = {
54
99
  // table only carried session+total and broke any widget configured for
55
100
  // week/month because `{amount}` fell through to the unscoped placeholder
56
101
  // and rendered blank.
57
- "follower-session": { amount: "twitch_session_follower_count", count: "twitch_session_follower_count" },
58
- "follower-week": { amount: "twitch_week_follower_count", count: "twitch_week_follower_count" },
59
- "follower-month": { amount: "twitch_month_follower_count", count: "twitch_month_follower_count" },
60
- "follower-total": { amount: "twitch_total_follower_count", count: "twitch_total_follower_count" },
61
- "subscriber-session": { amount: "twitch_session_subscribers_count", count: "twitch_session_subscribers_count" },
62
- "subscriber-week": { amount: "twitch_week_subscriber_count", count: "twitch_week_subscriber_count" },
63
- "subscriber-month": { amount: "twitch_month_subscriber_count", count: "twitch_month_subscriber_count" },
64
- "subscriber-total": { amount: "twitch_total_subscriber_count", count: "twitch_total_subscriber_count" },
65
- "subscriber-new-session": { amount: "twitch_session_new_subscribers_count", count: "twitch_session_new_subscribers_count" },
66
- "subscriber-resub-session": { amount: "twitch_session_resub_subscribers_count", count: "twitch_session_resub_subscribers_count" },
67
- "subscriber-gifted-session": { amount: "twitch_session_gifted_subscribers_count", count: "twitch_session_gifted_subscribers_count" },
68
- "cheer-session": { amount: "twitch_session_bits_count", count: "twitch_session_bits_count" },
69
- "cheer-week": { amount: "twitch_week_bits_count", count: "twitch_week_bits_count" },
70
- "cheer-month": { amount: "twitch_month_bits_count", count: "twitch_month_bits_count" },
71
- "cheer-total": { amount: "twitch_total_bits_count", count: "twitch_total_bits_count" },
72
- "tip-session": { amount: "session_donation_amount", count: "session_donation_count", currency: "session_donation_amount_currency_symbol" },
73
- "tip-week": { amount: "week_donation_amount", count: "week_donation_count", currency: "week_donation_amount_currency_symbol" },
74
- "tip-month": { amount: "month_donation_amount", count: "month_donation_count", currency: "month_donation_amount_currency_symbol" },
75
- "tip-total": { amount: "total_donation_amount", count: "total_donation_amount", currency: "total_donation_amount_currency_symbol" },
102
+ "follower-session": {
103
+ amount: "twitch_session_follower_count",
104
+ count: "twitch_session_follower_count"
105
+ },
106
+ "follower-week": {
107
+ amount: "twitch_week_follower_count",
108
+ count: "twitch_week_follower_count"
109
+ },
110
+ "follower-month": {
111
+ amount: "twitch_month_follower_count",
112
+ count: "twitch_month_follower_count"
113
+ },
114
+ "follower-total": {
115
+ amount: "twitch_total_follower_count",
116
+ count: "twitch_total_follower_count"
117
+ },
118
+ "subscriber-session": {
119
+ amount: "twitch_session_subscribers_count",
120
+ count: "twitch_session_subscribers_count"
121
+ },
122
+ "subscriber-week": {
123
+ amount: "twitch_week_subscriber_count",
124
+ count: "twitch_week_subscriber_count"
125
+ },
126
+ "subscriber-month": {
127
+ amount: "twitch_month_subscriber_count",
128
+ count: "twitch_month_subscriber_count"
129
+ },
130
+ "subscriber-total": {
131
+ amount: "twitch_total_subscriber_count",
132
+ count: "twitch_total_subscriber_count"
133
+ },
134
+ "subscriber-new-session": {
135
+ amount: "twitch_session_new_subscribers_count",
136
+ count: "twitch_session_new_subscribers_count"
137
+ },
138
+ "subscriber-resub-session": {
139
+ amount: "twitch_session_resub_subscribers_count",
140
+ count: "twitch_session_resub_subscribers_count"
141
+ },
142
+ "subscriber-gifted-session": {
143
+ amount: "twitch_session_gifted_subscribers_count",
144
+ count: "twitch_session_gifted_subscribers_count"
145
+ },
146
+ "cheer-session": {
147
+ amount: "twitch_session_bits_count",
148
+ count: "twitch_session_bits_count"
149
+ },
150
+ "cheer-week": {
151
+ amount: "twitch_week_bits_count",
152
+ count: "twitch_week_bits_count"
153
+ },
154
+ "cheer-month": {
155
+ amount: "twitch_month_bits_count",
156
+ count: "twitch_month_bits_count"
157
+ },
158
+ "cheer-total": {
159
+ amount: "twitch_total_bits_count",
160
+ count: "twitch_total_bits_count"
161
+ },
162
+ "tip-session": {
163
+ amount: "session_donation_amount",
164
+ count: "session_donation_count",
165
+ currency: "session_donation_amount_currency_symbol"
166
+ },
167
+ "tip-week": {
168
+ amount: "week_donation_amount",
169
+ count: "week_donation_count",
170
+ currency: "week_donation_amount_currency_symbol"
171
+ },
172
+ "tip-month": {
173
+ amount: "month_donation_amount",
174
+ count: "month_donation_count",
175
+ currency: "month_donation_amount_currency_symbol"
176
+ },
177
+ "tip-total": {
178
+ amount: "total_donation_amount",
179
+ count: "total_donation_amount",
180
+ currency: "total_donation_amount_currency_symbol"
181
+ },
76
182
  "tip-count": { amount: "donation_count", count: "donation_count" },
77
183
  // Top cheer / cheerer / donator readouts — per period. SE's `se-widget-top-*`
78
184
  // widgets encode the chosen period (session / weekly / monthly / alltime) in
@@ -88,26 +194,118 @@ var TWITCH_CONTEXTS = {
88
194
  // "the user who made the top donation", so the two rows below carry the
89
195
  // same name/username mapping — anything else falls through to the unscoped
90
196
  // `{{username}}` and renders blank.
91
- "cheer-session-top-donation": { name: "twitch_session_top_cheerer", username: "twitch_session_top_cheerer", amount: "twitch_session_top_cheer_amount", count: "twitch_session_top_cheer_amount" },
92
- "cheer-session-top-donator": { name: "twitch_session_top_cheerer", username: "twitch_session_top_cheerer", amount: "twitch_session_top_cheerer_amount", count: "twitch_session_top_cheerer_amount" },
93
- "cheer-weekly-top-donation": { name: "twitch_week_top_cheerer", username: "twitch_week_top_cheerer", amount: "twitch_week_top_cheerer_amount", count: "twitch_week_top_cheerer_amount" },
94
- "cheer-weekly-top-donator": { name: "twitch_week_top_cheerer", username: "twitch_week_top_cheerer", amount: "twitch_week_top_cheerer_amount", count: "twitch_week_top_cheerer_amount" },
95
- "cheer-monthly-top-donation": { name: "twitch_month_top_cheerer", username: "twitch_month_top_cheerer", amount: "twitch_month_top_cheerer_amount", count: "twitch_month_top_cheerer_amount" },
96
- "cheer-monthly-top-donator": { name: "twitch_month_top_cheerer", username: "twitch_month_top_cheerer", amount: "twitch_month_top_cheerer_amount", count: "twitch_month_top_cheerer_amount" },
97
- "cheer-alltime-top-donation": { name: "twitch_alltime_top_cheerer", username: "twitch_alltime_top_cheerer", amount: "twitch_alltime_top_cheerer_amount", count: "twitch_alltime_top_cheerer_amount" },
98
- "cheer-alltime-top-donator": { name: "twitch_alltime_top_cheerer", username: "twitch_alltime_top_cheerer", amount: "twitch_alltime_top_cheerer_amount", count: "twitch_alltime_top_cheerer_amount" },
99
- "tip-session-top-donation": { name: "session_top_donator", username: "session_top_donator", amount: "session_top_donator_amount", count: "session_top_donator_amount", currency: "session_top_donator_currency_symbol" },
100
- "tip-session-top-donator": { name: "session_top_donator", username: "session_top_donator", amount: "session_top_donator_amount", count: "session_top_donator_amount", currency: "session_top_donator_currency_symbol" },
101
- "tip-weekly-top-donation": { name: "week_top_donator", username: "week_top_donator", amount: "week_top_donator_amount", count: "week_top_donator_amount", currency: "week_top_donator_currency_symbol" },
102
- "tip-weekly-top-donator": { name: "week_top_donator", username: "week_top_donator", amount: "week_top_donator_amount", count: "week_top_donator_amount", currency: "week_top_donator_currency_symbol" },
103
- "tip-monthly-top-donation": { name: "month_top_donator", username: "month_top_donator", amount: "month_top_donator_amount", count: "month_top_donator_amount", currency: "month_top_donator_currency_symbol" },
104
- "tip-monthly-top-donator": { name: "month_top_donator", username: "month_top_donator", amount: "month_top_donator_amount", count: "month_top_donator_amount", currency: "month_top_donator_currency_symbol" },
105
- "tip-alltime-top-donation": { name: "top_donator", username: "top_donator", amount: "top_donator_amount", count: "top_donator_amount", currency: "top_donator_currency_symbol" },
106
- "tip-alltime-top-donator": { name: "top_donator", username: "top_donator", amount: "top_donator_amount", count: "top_donator_amount", currency: "top_donator_currency_symbol" },
197
+ "cheer-session-top-donation": {
198
+ name: "twitch_session_top_cheerer",
199
+ username: "twitch_session_top_cheerer",
200
+ amount: "twitch_session_top_cheer_amount",
201
+ count: "twitch_session_top_cheer_amount"
202
+ },
203
+ "cheer-session-top-donator": {
204
+ name: "twitch_session_top_cheerer",
205
+ username: "twitch_session_top_cheerer",
206
+ amount: "twitch_session_top_cheerer_amount",
207
+ count: "twitch_session_top_cheerer_amount"
208
+ },
209
+ "cheer-weekly-top-donation": {
210
+ name: "twitch_week_top_cheerer",
211
+ username: "twitch_week_top_cheerer",
212
+ amount: "twitch_week_top_cheerer_amount",
213
+ count: "twitch_week_top_cheerer_amount"
214
+ },
215
+ "cheer-weekly-top-donator": {
216
+ name: "twitch_week_top_cheerer",
217
+ username: "twitch_week_top_cheerer",
218
+ amount: "twitch_week_top_cheerer_amount",
219
+ count: "twitch_week_top_cheerer_amount"
220
+ },
221
+ "cheer-monthly-top-donation": {
222
+ name: "twitch_month_top_cheerer",
223
+ username: "twitch_month_top_cheerer",
224
+ amount: "twitch_month_top_cheerer_amount",
225
+ count: "twitch_month_top_cheerer_amount"
226
+ },
227
+ "cheer-monthly-top-donator": {
228
+ name: "twitch_month_top_cheerer",
229
+ username: "twitch_month_top_cheerer",
230
+ amount: "twitch_month_top_cheerer_amount",
231
+ count: "twitch_month_top_cheerer_amount"
232
+ },
233
+ "cheer-alltime-top-donation": {
234
+ name: "twitch_alltime_top_cheerer",
235
+ username: "twitch_alltime_top_cheerer",
236
+ amount: "twitch_alltime_top_cheerer_amount",
237
+ count: "twitch_alltime_top_cheerer_amount"
238
+ },
239
+ "cheer-alltime-top-donator": {
240
+ name: "twitch_alltime_top_cheerer",
241
+ username: "twitch_alltime_top_cheerer",
242
+ amount: "twitch_alltime_top_cheerer_amount",
243
+ count: "twitch_alltime_top_cheerer_amount"
244
+ },
245
+ "tip-session-top-donation": {
246
+ name: "session_top_donator",
247
+ username: "session_top_donator",
248
+ amount: "session_top_donator_amount",
249
+ count: "session_top_donator_amount",
250
+ currency: "session_top_donator_currency_symbol"
251
+ },
252
+ "tip-session-top-donator": {
253
+ name: "session_top_donator",
254
+ username: "session_top_donator",
255
+ amount: "session_top_donator_amount",
256
+ count: "session_top_donator_amount",
257
+ currency: "session_top_donator_currency_symbol"
258
+ },
259
+ "tip-weekly-top-donation": {
260
+ name: "week_top_donator",
261
+ username: "week_top_donator",
262
+ amount: "week_top_donator_amount",
263
+ count: "week_top_donator_amount",
264
+ currency: "week_top_donator_currency_symbol"
265
+ },
266
+ "tip-weekly-top-donator": {
267
+ name: "week_top_donator",
268
+ username: "week_top_donator",
269
+ amount: "week_top_donator_amount",
270
+ count: "week_top_donator_amount",
271
+ currency: "week_top_donator_currency_symbol"
272
+ },
273
+ "tip-monthly-top-donation": {
274
+ name: "month_top_donator",
275
+ username: "month_top_donator",
276
+ amount: "month_top_donator_amount",
277
+ count: "month_top_donator_amount",
278
+ currency: "month_top_donator_currency_symbol"
279
+ },
280
+ "tip-monthly-top-donator": {
281
+ name: "month_top_donator",
282
+ username: "month_top_donator",
283
+ amount: "month_top_donator_amount",
284
+ count: "month_top_donator_amount",
285
+ currency: "month_top_donator_currency_symbol"
286
+ },
287
+ "tip-alltime-top-donation": {
288
+ name: "top_donator",
289
+ username: "top_donator",
290
+ amount: "top_donator_amount",
291
+ count: "top_donator_amount",
292
+ currency: "top_donator_currency_symbol"
293
+ },
294
+ "tip-alltime-top-donator": {
295
+ name: "top_donator",
296
+ username: "top_donator",
297
+ amount: "top_donator_amount",
298
+ count: "top_donator_amount",
299
+ currency: "top_donator_currency_symbol"
300
+ },
107
301
  // Botcounter — SE uses `{wins}` for the running chat count; the dispatcher's
108
302
  // fallbackVar for `se-widget-botcounter` is `twitch_session_chat_count`, so
109
303
  // when the listener arg lands as that var name we treat it as the wins source.
110
- twitch_session_chat_count: { wins: "twitch_session_chat_count", count: "twitch_session_chat_count", amount: "twitch_session_chat_count" },
304
+ twitch_session_chat_count: {
305
+ wins: "twitch_session_chat_count",
306
+ count: "twitch_session_chat_count",
307
+ amount: "twitch_session_chat_count"
308
+ },
111
309
  // Merch readouts — SE used to ship via their own shop, now most users route
112
310
  // through Fourthwall. Lumia exposes `fourthwall_last_order` as a full object:
113
311
  // { username, email, friendlyId, amount, currency, message, items,
@@ -136,45 +334,151 @@ var TWITCH_CONTEXTS = {
136
334
  }
137
335
  };
138
336
  var KICK_CONTEXTS = {
139
- "follower-latest": { name: "kick_last_follower", username: "kick_last_follower" },
140
- "subscriber-latest": { name: "kick_last_subscriber", username: "kick_last_subscriber" },
141
- "subscriber-new-latest": { name: "kick_last_subscriber", username: "kick_last_subscriber" },
142
- "subscriber-resub-latest": { name: "kick_last_subscriber", username: "kick_last_subscriber" },
337
+ "follower-latest": {
338
+ name: "kick_last_follower",
339
+ username: "kick_last_follower"
340
+ },
341
+ "subscriber-latest": {
342
+ name: "kick_last_subscriber",
343
+ username: "kick_last_subscriber"
344
+ },
345
+ "subscriber-new-latest": {
346
+ name: "kick_last_subscriber",
347
+ username: "kick_last_subscriber"
348
+ },
349
+ "subscriber-resub-latest": {
350
+ name: "kick_last_subscriber",
351
+ username: "kick_last_subscriber"
352
+ },
143
353
  // Mirror the Twitch split: `{sender}` = giver, `{name}` = recipient.
144
354
  // `kick_last_gifted` was added alongside this mapping; see twitch context
145
355
  // above for the rationale (without the split, SE's "{sender} gifted to
146
356
  // {name}" renders the gifter on both sides).
147
- "subscriber-gifted-latest": { name: "kick_last_gifted", username: "kick_last_gifted", sender: "kick_last_gifter", amount: "kick_last_gifter_amount", count: "kick_last_gifter_amount" },
148
- "cheer-latest": { name: "kick_last_kicks", username: "kick_last_kicks", amount: "kick_last_kicks_amount", count: "kick_last_kicks_amount" },
149
- "raid-latest": { name: "kick_last_host", username: "kick_last_host", amount: "kick_last_host_amount", count: "kick_last_host_amount" },
150
- "follower-session": { amount: "kick_session_follower_count", count: "kick_session_follower_count" },
151
- "subscriber-session": { amount: "kick_session_subscriber_count", count: "kick_session_subscriber_count" },
152
- "cheer-session": { amount: "kick_session_kicks_count", count: "kick_session_kicks_count" },
153
- "subscriber-gifted-session": { amount: "kick_session_gifts_count", count: "kick_session_gifts_count" },
154
- "follower-total": { amount: "kick_total_follower_count", count: "kick_total_follower_count" },
155
- "subscriber-total": { amount: "kick_total_subscriber_count", count: "kick_total_subscriber_count" },
156
- "cheer-total": { amount: "kick_total_kicks_count", count: "kick_total_kicks_count" },
157
- "follower-week": { amount: "kick_week_follower_count", count: "kick_week_follower_count" },
158
- "follower-month": { amount: "kick_month_follower_count", count: "kick_month_follower_count" },
159
- "subscriber-week": { amount: "kick_week_subscriber_count", count: "kick_week_subscriber_count" },
160
- "subscriber-month": { amount: "kick_month_subscriber_count", count: "kick_month_subscriber_count" }
357
+ "subscriber-gifted-latest": {
358
+ name: "kick_last_gifted",
359
+ username: "kick_last_gifted",
360
+ sender: "kick_last_gifter",
361
+ amount: "kick_last_gifter_amount",
362
+ count: "kick_last_gifter_amount"
363
+ },
364
+ "cheer-latest": {
365
+ name: "kick_last_kicks",
366
+ username: "kick_last_kicks",
367
+ amount: "kick_last_kicks_amount",
368
+ count: "kick_last_kicks_amount"
369
+ },
370
+ "raid-latest": {
371
+ name: "kick_last_host",
372
+ username: "kick_last_host",
373
+ amount: "kick_last_host_amount",
374
+ count: "kick_last_host_amount"
375
+ },
376
+ "follower-session": {
377
+ amount: "kick_session_follower_count",
378
+ count: "kick_session_follower_count"
379
+ },
380
+ "subscriber-session": {
381
+ amount: "kick_session_subscriber_count",
382
+ count: "kick_session_subscriber_count"
383
+ },
384
+ "cheer-session": {
385
+ amount: "kick_session_kicks_count",
386
+ count: "kick_session_kicks_count"
387
+ },
388
+ "subscriber-gifted-session": {
389
+ amount: "kick_session_gifts_count",
390
+ count: "kick_session_gifts_count"
391
+ },
392
+ "follower-total": {
393
+ amount: "kick_total_follower_count",
394
+ count: "kick_total_follower_count"
395
+ },
396
+ "subscriber-total": {
397
+ amount: "kick_total_subscriber_count",
398
+ count: "kick_total_subscriber_count"
399
+ },
400
+ "cheer-total": {
401
+ amount: "kick_total_kicks_count",
402
+ count: "kick_total_kicks_count"
403
+ },
404
+ "follower-week": {
405
+ amount: "kick_week_follower_count",
406
+ count: "kick_week_follower_count"
407
+ },
408
+ "follower-month": {
409
+ amount: "kick_month_follower_count",
410
+ count: "kick_month_follower_count"
411
+ },
412
+ "subscriber-week": {
413
+ amount: "kick_week_subscriber_count",
414
+ count: "kick_week_subscriber_count"
415
+ },
416
+ "subscriber-month": {
417
+ amount: "kick_month_subscriber_count",
418
+ count: "kick_month_subscriber_count"
419
+ }
161
420
  };
162
421
  var YOUTUBE_CONTEXTS = {
163
- "follower-latest": { name: "youtube_last_subscriber", username: "youtube_last_subscriber" },
164
- "subscriber-latest": { name: "youtube_last_member", username: "youtube_last_member" },
165
- "subscriber-new-latest": { name: "youtube_last_member", username: "youtube_last_member" },
166
- "subscriber-resub-latest": { name: "youtube_last_member", username: "youtube_last_member" },
167
- "subscriber-gifted-latest": { name: "youtube_last_gift_member", username: "youtube_last_gift_member" },
168
- "cheer-latest": { name: "youtube_last_superchatter", username: "youtube_last_superchatter" },
169
- "follower-session": { amount: "youtube_session_subscriber_count", count: "youtube_session_subscriber_count" },
170
- "subscriber-session": { amount: "youtube_session_member_count", count: "youtube_session_member_count" },
171
- "cheer-session": { amount: "youtube_session_superchat_count", count: "youtube_session_superchat_count" },
172
- "follower-total": { amount: "youtube_total_subscriber_count", count: "youtube_total_subscriber_count" },
173
- "subscriber-total": { amount: "youtube_total_member_count", count: "youtube_total_member_count" },
174
- "follower-week": { amount: "youtube_week_subscriber_count", count: "youtube_week_subscriber_count" },
175
- "follower-month": { amount: "youtube_month_subscriber_count", count: "youtube_month_subscriber_count" },
176
- "subscriber-week": { amount: "youtube_week_member_count", count: "youtube_week_member_count" },
177
- "subscriber-month": { amount: "youtube_month_member_count", count: "youtube_month_member_count" }
422
+ "follower-latest": {
423
+ name: "youtube_last_subscriber",
424
+ username: "youtube_last_subscriber"
425
+ },
426
+ "subscriber-latest": {
427
+ name: "youtube_last_member",
428
+ username: "youtube_last_member"
429
+ },
430
+ "subscriber-new-latest": {
431
+ name: "youtube_last_member",
432
+ username: "youtube_last_member"
433
+ },
434
+ "subscriber-resub-latest": {
435
+ name: "youtube_last_member",
436
+ username: "youtube_last_member"
437
+ },
438
+ "subscriber-gifted-latest": {
439
+ name: "youtube_last_gift_member",
440
+ username: "youtube_last_gift_member"
441
+ },
442
+ "cheer-latest": {
443
+ name: "youtube_last_superchatter",
444
+ username: "youtube_last_superchatter"
445
+ },
446
+ "follower-session": {
447
+ amount: "youtube_session_subscriber_count",
448
+ count: "youtube_session_subscriber_count"
449
+ },
450
+ "subscriber-session": {
451
+ amount: "youtube_session_member_count",
452
+ count: "youtube_session_member_count"
453
+ },
454
+ "cheer-session": {
455
+ amount: "youtube_session_superchat_count",
456
+ count: "youtube_session_superchat_count"
457
+ },
458
+ "follower-total": {
459
+ amount: "youtube_total_subscriber_count",
460
+ count: "youtube_total_subscriber_count"
461
+ },
462
+ "subscriber-total": {
463
+ amount: "youtube_total_member_count",
464
+ count: "youtube_total_member_count"
465
+ },
466
+ "follower-week": {
467
+ amount: "youtube_week_subscriber_count",
468
+ count: "youtube_week_subscriber_count"
469
+ },
470
+ "follower-month": {
471
+ amount: "youtube_month_subscriber_count",
472
+ count: "youtube_month_subscriber_count"
473
+ },
474
+ "subscriber-week": {
475
+ amount: "youtube_week_member_count",
476
+ count: "youtube_week_member_count"
477
+ },
478
+ "subscriber-month": {
479
+ amount: "youtube_month_member_count",
480
+ count: "youtube_month_member_count"
481
+ }
178
482
  };
179
483
  var RECENT_TO_LATEST_LISTENER = {
180
484
  "follower-recent": "follower-latest",
@@ -226,11 +530,14 @@ function translateSeText(input, listener, passthrough, provider, options) {
226
530
  if (!input) return "";
227
531
  const ctx = listener ? listenerPlaceholderContext(listener, provider) : {};
228
532
  const useAlertFallback = (options?.context ?? "alert") === "alert";
229
- return input.replace(/\{([a-zA-Z][a-zA-Z0-9_.]*)\}/g, (_match, raw) => {
230
- if (passthrough?.has(raw)) return `{{${raw}}}`;
231
- const mapped = ctx[raw] ?? (useAlertFallback ? SE_TO_LUMIA_PLACEHOLDER[raw] : void 0) ?? raw;
232
- return `{{${mapped}}}`;
233
- });
533
+ return input.replace(
534
+ /\{([a-zA-Z][a-zA-Z0-9_.]*)\}/g,
535
+ (_match, raw) => {
536
+ if (passthrough?.has(raw)) return `{{${raw}}}`;
537
+ const mapped = ctx[raw] ?? (useAlertFallback ? SE_TO_LUMIA_PLACEHOLDER[raw] : void 0) ?? raw;
538
+ return `{{${mapped}}}`;
539
+ }
540
+ );
234
541
  }
235
542
  var KICK_LISTENER_VARS = {
236
543
  "follower-latest": "kick_last_follower",
@@ -273,8 +580,10 @@ var YOUTUBE_LISTENER_VARS = {
273
580
  function listenerToLumiaVariable(listener, provider = "twitch") {
274
581
  if (typeof listener !== "string" || !listener) return "username";
275
582
  const resolvedListener = RECENT_TO_LATEST_LISTENER[listener] ?? listener;
276
- if (provider === "kick" && KICK_LISTENER_VARS[resolvedListener]) return KICK_LISTENER_VARS[resolvedListener];
277
- if (provider === "youtube" && YOUTUBE_LISTENER_VARS[resolvedListener]) return YOUTUBE_LISTENER_VARS[resolvedListener];
583
+ if (provider === "kick" && KICK_LISTENER_VARS[resolvedListener])
584
+ return KICK_LISTENER_VARS[resolvedListener];
585
+ if (provider === "youtube" && YOUTUBE_LISTENER_VARS[resolvedListener])
586
+ return YOUTUBE_LISTENER_VARS[resolvedListener];
278
587
  const m = {
279
588
  // Latest events
280
589
  "follower-latest": "twitch_last_follower",
@@ -367,7 +676,9 @@ function seCssToBounds(css, defaults = { width: 600, height: 200 }, canvas) {
367
676
  let x = toPx(css.left, 0, "width");
368
677
  let y = toPx(css.top, 0, "height");
369
678
  if (typeof css.transform === "string" && css.transform.includes("translate")) {
370
- const m = /translate\s*\(\s*([^,)]+)\s*(?:,\s*([^)]+))?\s*\)/i.exec(css.transform);
679
+ const m = /translate\s*\(\s*([^,)]+)\s*(?:,\s*([^)]+))?\s*\)/i.exec(
680
+ css.transform
681
+ );
371
682
  if (m) {
372
683
  const parseShift = (raw, basis) => {
373
684
  if (!raw) return 0;
@@ -437,21 +748,33 @@ var LUMIA_DEFAULT_SIZES = {
437
748
  };
438
749
  function translateVariationCondition(seType, seCondition, requirement, seEventType) {
439
750
  if (seType === "gift") return { conditionType: "IS_GIFT", condition: 1 };
440
- if (seType === "communityGift") return { conditionType: "GIFT_SUB_GREATER", condition: requirement ?? 0 };
751
+ if (seType === "communityGift")
752
+ return { conditionType: "GIFT_SUB_GREATER", condition: requirement ?? 0 };
441
753
  const isSubscriberEvent = seEventType === "subscriber";
442
754
  if (isSubscriberEvent && seCondition === "ATLEAST" && requirement != null) {
443
- return { conditionType: "SUBSCRIBED_MONTHS_GREATER", condition: requirement };
755
+ return {
756
+ conditionType: "SUBSCRIBED_MONTHS_GREATER",
757
+ condition: requirement
758
+ };
444
759
  }
445
760
  if (isSubscriberEvent && seCondition === "EXACT" && requirement != null) {
446
761
  return { conditionType: "SUBSCRIBED_MONTHS_EQUAL", condition: requirement };
447
762
  }
448
- if (seCondition === "EXACT" && requirement != null) return { conditionType: "EQUAL_NUMBER", condition: requirement };
763
+ if (seCondition === "EXACT" && requirement != null)
764
+ return { conditionType: "EQUAL_NUMBER", condition: requirement };
449
765
  if (seCondition === "ATLEAST" && requirement != null) {
450
766
  return { conditionType: "GREATER_NUMBER", condition: requirement };
451
767
  }
452
768
  return { conditionType: "RANDOM", condition: 100 };
453
769
  }
454
- var LUMIA_LAYOUTS = /* @__PURE__ */ new Set(["column", "row", "onlyImage", "onlyText", "imageOver", "textOver"]);
770
+ var LUMIA_LAYOUTS = /* @__PURE__ */ new Set([
771
+ "column",
772
+ "row",
773
+ "onlyImage",
774
+ "onlyText",
775
+ "imageOver",
776
+ "textOver"
777
+ ]);
455
778
  var SE_LAYOUT_ALIASES = {
456
779
  behind: "textOver"
457
780
  };
@@ -478,13 +801,27 @@ function mapSeTextCssToModuleCss(widget, defaults = {}) {
478
801
  return fallback;
479
802
  };
480
803
  return {
481
- lineHeight: asNumOrStr(seTextCss["line-height"], defaults.lineHeight ?? 1),
804
+ lineHeight: asNumOrStr(
805
+ seTextCss["line-height"],
806
+ defaults.lineHeight ?? "normal"
807
+ ),
482
808
  fontSize: asNumOrStr(seTextCss["font-size"], defaults.fontSize ?? 24),
483
809
  textAlign: asStr(seTextCss["text-align"], defaults.textAlign ?? "center"),
484
- fontFamily: asStr(seTextCss["font-family"], defaults.fontFamily ?? "Roboto"),
810
+ fontFamily: asStr(
811
+ seTextCss["font-family"],
812
+ defaults.fontFamily ?? "Roboto"
813
+ ),
485
814
  fontWeight: asStr(seTextCss["font-weight"], defaults.fontWeight ?? "bold"),
815
+ fontStyle: asStr(seTextCss["font-style"], defaults.fontStyle ?? "normal"),
816
+ textTransform: asStr(
817
+ seTextCss["text-transform"],
818
+ defaults.textTransform ?? "none"
819
+ ),
486
820
  color: asStr(seTextCss.color, defaults.color ?? "#ffffff"),
487
- textShadow: asStr(seTextCss["text-shadow"], defaults.textShadow ?? "rgb(0, 0, 0) 1px 1px 1px"),
821
+ textShadow: asStr(
822
+ seTextCss["text-shadow"],
823
+ defaults.textShadow ?? "rgb(0, 0, 0) 1px 1px 1px"
824
+ ),
488
825
  background: defaults.background ?? "transparent"
489
826
  };
490
827
  }
@@ -533,10 +870,16 @@ function buildUnit(widget, lumiaType, moduleExtras, ctx, sizeOverride) {
533
870
  // src/se-import/mappers/basic.ts
534
871
  function seExtraTextCss(seCss, scrolling) {
535
872
  const extra = {};
536
- if (typeof seCss["text-decoration"] === "string") extra.textDecoration = seCss["text-decoration"];
537
- if (typeof seCss["text-transform"] === "string") extra.textTransform = seCss["text-transform"];
538
- if (seCss["-webkit-text-stroke-color"]) extra["-webkit-text-stroke-color"] = seCss["-webkit-text-stroke-color"];
539
- if (seCss["-webkit-text-stroke-width"] != null) extra["-webkit-text-stroke-width"] = seCss["-webkit-text-stroke-width"];
873
+ if (typeof seCss["text-decoration"] === "string")
874
+ extra.textDecoration = seCss["text-decoration"];
875
+ if (typeof seCss["text-transform"] === "string")
876
+ extra.textTransform = seCss["text-transform"];
877
+ if (typeof seCss["font-style"] === "string")
878
+ extra.fontStyle = seCss["font-style"];
879
+ if (seCss["-webkit-text-stroke-color"])
880
+ extra["-webkit-text-stroke-color"] = seCss["-webkit-text-stroke-color"];
881
+ if (seCss["-webkit-text-stroke-width"] != null)
882
+ extra["-webkit-text-stroke-width"] = seCss["-webkit-text-stroke-width"];
540
883
  if (scrolling?.enabled) {
541
884
  extra.scroll = true;
542
885
  const speed = Number(scrolling.speed);
@@ -545,54 +888,82 @@ function seExtraTextCss(seCss, scrolling) {
545
888
  return extra;
546
889
  }
547
890
  function mapText(widget, ctx) {
548
- const value = translateSeText(widget.text?.value ?? "", null, void 0, ctx?.provider, { context: "static" });
891
+ const value = translateSeText(
892
+ widget.text?.value ?? "",
893
+ null,
894
+ void 0,
895
+ ctx?.provider,
896
+ { context: "static" }
897
+ );
549
898
  const seCss = widget.text?.css ?? {};
550
- return buildUnit(widget, "text", {
551
- content: {
552
- value,
553
- highlightColor: "inherit"
899
+ return buildUnit(
900
+ widget,
901
+ "text",
902
+ {
903
+ content: {
904
+ value,
905
+ highlightColor: "inherit"
906
+ },
907
+ css: {
908
+ fontSize: seCss["font-size"] ?? 24,
909
+ textAlign: seCss["text-align"] ?? "center",
910
+ fontFamily: seCss["font-family"] ?? "Roboto",
911
+ fontWeight: seCss["font-weight"] ?? "bold",
912
+ color: seCss["color"] ?? "#ffffff",
913
+ textShadow: seCss["text-shadow"] ?? "rgb(0, 0, 0) 1px 1px 1px",
914
+ lineHeight: seCss["line-height"] ?? "normal",
915
+ background: "transparent",
916
+ ...seExtraTextCss(seCss, widget.text?.scrolling)
917
+ }
554
918
  },
555
- css: {
556
- fontSize: seCss["font-size"] ?? 24,
557
- textAlign: seCss["text-align"] ?? "center",
558
- fontFamily: seCss["font-family"] ?? "Roboto",
559
- fontWeight: seCss["font-weight"] ?? "bold",
560
- color: seCss["color"] ?? "#ffffff",
561
- textShadow: seCss["text-shadow"] ?? "rgb(0, 0, 0) 1px 1px 1px",
562
- lineHeight: seCss["line-height"] ?? 1,
563
- background: "transparent",
564
- ...seExtraTextCss(seCss, widget.text?.scrolling)
565
- }
566
- }, ctx);
919
+ ctx
920
+ );
567
921
  }
568
922
  function mapImage(widget, ctx) {
569
- return buildUnit(widget, "image", {
570
- content: {
571
- src: widget.image?.src ?? "",
572
- loop: true
573
- }
574
- }, ctx);
923
+ return buildUnit(
924
+ widget,
925
+ "image",
926
+ {
927
+ content: {
928
+ src: widget.image?.src ?? "",
929
+ loop: true
930
+ }
931
+ },
932
+ ctx
933
+ );
575
934
  }
576
935
  function mapVideo(widget, ctx) {
577
- return buildUnit(widget, "video", {
578
- content: {
579
- src: widget.video?.src ?? "",
580
- volume: widget.video?.volume ?? 0.5,
581
- loop: true,
582
- muted: false
583
- }
584
- }, ctx);
936
+ const unit = buildUnit(
937
+ widget,
938
+ "video",
939
+ {
940
+ content: {
941
+ src: widget.video?.src ?? "",
942
+ volume: widget.video?.volume ?? 0.5,
943
+ loop: true,
944
+ muted: false
945
+ }
946
+ },
947
+ ctx
948
+ );
949
+ unit.layer.bounds.autoHeight = true;
950
+ return unit;
585
951
  }
586
952
  var LUMIA_SNOW_WEBM = "https://storage.lumiastream.com/overlays/global/seasonal/snow.webm";
587
953
  function mapSnow(widget, ctx) {
588
- return buildUnit(widget, "video", {
589
- content: {
590
- src: LUMIA_SNOW_WEBM,
591
- volume: 0,
592
- loop: true,
593
- muted: true
594
- }
595
- }, ctx);
954
+ return buildUnit(
955
+ widget,
956
+ "video",
957
+ {
958
+ content: {
959
+ src: LUMIA_SNOW_WEBM,
960
+ volume: 0,
961
+ loop: true,
962
+ muted: true
963
+ }
964
+ },
965
+ ctx
966
+ );
596
967
  }
597
968
  function mapReadout(widget, fallbackVar, ctx) {
598
969
  const explicitListener = widget.variables?.listener;
@@ -602,7 +973,9 @@ function mapReadout(widget, fallbackVar, ctx) {
602
973
  const variable = listenerToLumiaVariable(listener, ctx?.provider);
603
974
  const userTemplate = widget.text?.value ?? "";
604
975
  const inheritsText = userTemplate.trim().length > 0 && /\{[a-zA-Z]/.test(userTemplate);
605
- const value = inheritsText ? translateSeText(userTemplate, listener, void 0, ctx?.provider, { context: "static" }) : `{{${variable}}}`;
976
+ const value = inheritsText ? translateSeText(userTemplate, listener, void 0, ctx?.provider, {
977
+ context: "static"
978
+ }) : `{{${variable}}}`;
606
979
  const seCss = widget.text?.css ?? {};
607
980
  return buildUnit(
608
981
  widget,
@@ -616,7 +989,7 @@ function mapReadout(widget, fallbackVar, ctx) {
616
989
  fontWeight: seCss["font-weight"] ?? "bold",
617
990
  color: seCss["color"] ?? "#ffffff",
618
991
  textShadow: seCss["text-shadow"] ?? "rgb(0, 0, 0) 1px 1px 1px",
619
- lineHeight: seCss["line-height"] ?? 1,
992
+ lineHeight: seCss["line-height"] ?? "normal",
620
993
  background: "transparent",
621
994
  ...seExtraTextCss(seCss, widget.text?.scrolling)
622
995
  }
@@ -1081,9 +1454,11 @@ function mapGoal(widget, ctx) {
1081
1454
  source: charity ? charity.source : sourceFromListener(listener),
1082
1455
  image: "",
1083
1456
  audio: { decrement: "", increment: "", goal: "", goalFailed: "", volume: 1 },
1084
- // `null` (rather than `''`) is the cleaner contract — Goal/index.tsx:11's
1085
- // `isGoalExpired` treats any falsy as not-expired so both work at render time,
1086
- // but `null` survives JSON round-trips without ambiguity ('' vs unset).
1457
+ // `null` (rather than `''`) is the cleaner contract — `formatEndsIn`
1458
+ // in Goal/index.tsx treats both as "no deadline" so both work at
1459
+ // render time, but `null` survives JSON round-trips without
1460
+ // ambiguity ('' vs unset). The Goal module no longer hides itself
1461
+ // when the date passes; the "Ends in…" sublabel just disappears.
1087
1462
  endDate: v.endDate ?? null,
1088
1463
  goalAnimation: "",
1089
1464
  goalAnimationLoop: true,
@@ -1485,15 +1860,13 @@ function computeAllowedEventsFromSEListeners(seListeners, explicitListener) {
1485
1860
  });
1486
1861
  return allowedAlerts;
1487
1862
  }
1488
- function seScrollSpeedToMarqueeDurationSec(seSpeed) {
1489
- const n = typeof seSpeed === "number" && Number.isFinite(seSpeed) ? seSpeed : 6;
1490
- const clamped = Math.min(20, Math.max(1, n));
1491
- const duration = 60 / clamped;
1492
- return Math.min(120, Math.max(5, Math.round(duration * 10) / 10));
1863
+ var LUMIA_DEFAULT_MARQUEE_PIXELS_PER_SECOND = 20;
1864
+ function seScrollSpeedToPixelsPerSecond(_seSpeed) {
1865
+ return LUMIA_DEFAULT_MARQUEE_PIXELS_PER_SECOND;
1493
1866
  }
1494
1867
  function seTemplateToMarqueeTemplate(seTemplate) {
1495
1868
  if (typeof seTemplate !== "string" || !seTemplate.trim()) return void 0;
1496
- return seTemplate.split("{name}").join("{username}").split("{username}").join("{username}");
1869
+ return translateSeText(seTemplate);
1497
1870
  }
1498
1871
  function mapEventList(widget, ctx) {
1499
1872
  const v = widget.variables ?? {};
@@ -1506,7 +1879,7 @@ function mapEventList(widget, ctx) {
1506
1879
  const scrolling = seText?.scrolling;
1507
1880
  const isMarquee = !!(scrolling && scrolling.enabled === true);
1508
1881
  const itemTemplate = isMarquee ? seTemplateToMarqueeTemplate(seText?.value) : void 0;
1509
- const marqueeSpeed = isMarquee ? seScrollSpeedToMarqueeDurationSec(scrolling?.speed) : void 0;
1882
+ const marqueeSpeed = isMarquee ? seScrollSpeedToPixelsPerSecond(scrolling?.speed) : void 0;
1510
1883
  const marqueeDirection = isMarquee ? scrolling?.direction === "right" ? "right" : "left" : void 0;
1511
1884
  const theme = isMarquee ? "marquee" : v.theme ?? "simple";
1512
1885
  return buildUnit(
@@ -1941,6 +2314,47 @@ function mapTimer(widget, ctx) {
1941
2314
  function mapNowPlaying(widget, ctx) {
1942
2315
  const v = widget.variables ?? {};
1943
2316
  const source = v.provider === "youtube" ? "youtubemusic" : "spotify";
2317
+ const isMarquee = !!v.simpleDesign;
2318
+ const text = widget.text ?? {};
2319
+ const scrolling = text.scrolling ?? {};
2320
+ const seTextCss = text.css ?? {};
2321
+ const seTemplate = typeof text.value === "string" && text.value.trim().length > 0 ? text.value : "";
2322
+ const itemTemplate = seTemplate ? seTemplate.replace(/\{title\}/gi, "{{songTitle}}").replace(/\{artist\}/gi, "{{songArtist}}") : "{{songTitle}} - {{songArtist}}";
2323
+ const marqueeSpeed = 20;
2324
+ const marqueeDirection = scrolling.direction === "right" ? "right" : "left";
2325
+ const wantsShadow = text.enableShadow !== false;
2326
+ const importedCss = {
2327
+ borderRadius: "0px",
2328
+ borderStyle: "solid",
2329
+ borderWidth: "0px",
2330
+ borderColor: "transparent",
2331
+ fontSize: seTextCss["font-size"] ?? 24,
2332
+ textAlign: seTextCss["text-align"] ?? "left",
2333
+ fontFamily: seTextCss["font-family"] ?? "Be Vietnam Pro",
2334
+ fontWeight: seTextCss["font-weight"] ?? "bold",
2335
+ color: seTextCss["color"] ?? "#ffffff",
2336
+ textShadow: wantsShadow ? seTextCss["text-shadow"] ?? "rgb(0, 0, 0) 1px 1px 1px" : "none",
2337
+ // `normal` (not `1`) when SE didn't specify — see the same fix in
2338
+ // `build.ts::mapSeTextCssToModuleCss` and `basic.ts::mapText` for the
2339
+ // rationale. This path is the now-playing / current-song mapper, which
2340
+ // is the one the user noticed had a too-tight line-height in the
2341
+ // marquee render.
2342
+ lineHeight: seTextCss["line-height"] ?? "normal",
2343
+ background: "transparent"
2344
+ };
2345
+ if (typeof seTextCss["text-transform"] === "string")
2346
+ importedCss.textTransform = seTextCss["text-transform"];
2347
+ if (typeof seTextCss["font-style"] === "string")
2348
+ importedCss.fontStyle = seTextCss["font-style"];
2349
+ if (typeof seTextCss["text-decoration"] === "string")
2350
+ importedCss.textDecoration = seTextCss["text-decoration"];
2351
+ if (typeof seTextCss["letter-spacing"] === "string" || typeof seTextCss["letter-spacing"] === "number") {
2352
+ importedCss.letterSpacing = seTextCss["letter-spacing"];
2353
+ }
2354
+ if (seTextCss["-webkit-text-stroke-color"])
2355
+ importedCss["-webkit-text-stroke-color"] = seTextCss["-webkit-text-stroke-color"];
2356
+ if (seTextCss["-webkit-text-stroke-width"] != null)
2357
+ importedCss["-webkit-text-stroke-width"] = seTextCss["-webkit-text-stroke-width"];
1944
2358
  return buildUnit(
1945
2359
  widget,
1946
2360
  "nowplaying",
@@ -1949,7 +2363,7 @@ function mapNowPlaying(widget, ctx) {
1949
2363
  version: 1,
1950
2364
  type: "nowplaying",
1951
2365
  source,
1952
- theme: "basic",
2366
+ theme: isMarquee ? "marquee" : "basic",
1953
2367
  borderRadius: "30px",
1954
2368
  songTitle: `{{${source}_now_playing_song=Lumia Dream}}`,
1955
2369
  songArtist: `{{${source}_now_playing_artist}}`,
@@ -1957,20 +2371,14 @@ function mapNowPlaying(widget, ctx) {
1957
2371
  showImg: true,
1958
2372
  oneLineTitle: false,
1959
2373
  highlightColor: "inherit",
2374
+ ...isMarquee ? {
2375
+ marqueeSpeed,
2376
+ marqueeDirection,
2377
+ themeConfig: { itemTemplate }
2378
+ } : {},
1960
2379
  importMeta: buildImportMeta(widget, { simpleDesign: !!v.simpleDesign })
1961
2380
  },
1962
- css: {
1963
- borderRadius: "0px",
1964
- borderStyle: "solid",
1965
- borderWidth: "0px",
1966
- borderColor: "transparent",
1967
- lineHeight: 1,
1968
- textAlign: "left",
1969
- fontFamily: "Be Vietnam Pro",
1970
- color: "#ffffff",
1971
- textShadow: "rgb(0, 0, 0) 1px 1px 1px",
1972
- background: "transparent"
1973
- }
2381
+ css: importedCss
1974
2382
  },
1975
2383
  ctx
1976
2384
  );
@@ -2389,6 +2797,22 @@ function mapEmoteWall(widget, ctx) {
2389
2797
  );
2390
2798
  }
2391
2799
  function mapUnsupportedAsText(widget, ctx) {
2800
+ const userTemplate = typeof widget.text?.value === "string" ? widget.text.value.trim() : "";
2801
+ const inheritsText = userTemplate.length > 0 && /\{[a-zA-Z]/.test(userTemplate);
2802
+ if (inheritsText) {
2803
+ return buildUnit(
2804
+ widget,
2805
+ "text",
2806
+ {
2807
+ css: mapSeTextCssToModuleCss(widget, { fontSize: 24 }),
2808
+ content: {
2809
+ value: translateSeText(userTemplate),
2810
+ highlightColor: "inherit"
2811
+ }
2812
+ },
2813
+ ctx
2814
+ );
2815
+ }
2392
2816
  return buildUnit(
2393
2817
  widget,
2394
2818
  "text",
@@ -2732,7 +3156,13 @@ var READOUT_TYPES = {
2732
3156
  // twitch_session_chat_count, so we render it as a text layer with the variable inline.
2733
3157
  "se-widget-botcounter": "twitch_session_chat_count"
2734
3158
  };
2735
- var SEASONAL_IMAGE_TYPES = /* @__PURE__ */ new Set(["se-widget-halloween", "se-widget-xmas", "se-widget-valentine", "se-widget-easter", "se-widget-world-cup"]);
3159
+ var SEASONAL_IMAGE_TYPES = /* @__PURE__ */ new Set([
3160
+ "se-widget-halloween",
3161
+ "se-widget-xmas",
3162
+ "se-widget-valentine",
3163
+ "se-widget-easter",
3164
+ "se-widget-world-cup"
3165
+ ]);
2736
3166
  var GOAL_TYPES = /* @__PURE__ */ new Set([
2737
3167
  "se-widget-follower-goal",
2738
3168
  "se-widget-subscriber-goal",
@@ -2743,59 +3173,212 @@ var GOAL_TYPES = /* @__PURE__ */ new Set([
2743
3173
  "se-widget-tiltify-goal",
2744
3174
  "se-widget-merch-goal"
2745
3175
  ]);
2746
- var EVENTLIST_TYPES = /* @__PURE__ */ new Set(["se-widget-event-list", "se-widget-follower-recent", "se-widget-subscriber-recent", "se-widget-cheer-recent", "se-widget-tip-recent"]);
3176
+ var EVENTLIST_TYPES = /* @__PURE__ */ new Set([
3177
+ "se-widget-event-list",
3178
+ "se-widget-follower-recent",
3179
+ "se-widget-subscriber-recent",
3180
+ "se-widget-cheer-recent",
3181
+ "se-widget-tip-recent"
3182
+ ]);
2747
3183
  function dispatch(widget, ctx) {
2748
3184
  const t = widget.type;
2749
- console.log("[SE-IMPORT] dispatch widget", {
2750
- seType: t,
2751
- name: widget.name,
2752
- listener: widget.listener,
2753
- listeners: widget.listeners,
2754
- variableKeys: widget.variables ? Object.keys(widget.variables) : [],
2755
- hasCss: !!widget.css
2756
- });
2757
3185
  if (isWidgetFlaggedOff(t)) {
2758
- return { unit: mapUnsupportedAsText(widget, ctx), status: "placeholder", lumiaType: "text", flaggedOff: true };
3186
+ return {
3187
+ unit: mapUnsupportedAsText(widget, ctx),
3188
+ status: "placeholder",
3189
+ lumiaType: "text",
3190
+ flaggedOff: true
3191
+ };
2759
3192
  }
2760
- if (t === "text") return { unit: mapText(widget, ctx), status: "direct", lumiaType: "text" };
2761
- if (t === "image") return { unit: mapImage(widget, ctx), status: "direct", lumiaType: "image" };
2762
- if (t === "video") return { unit: mapVideo(widget, ctx), status: "direct", lumiaType: "video" };
3193
+ if (t === "text")
3194
+ return { unit: mapText(widget, ctx), status: "direct", lumiaType: "text" };
3195
+ if (t === "image")
3196
+ return {
3197
+ unit: mapImage(widget, ctx),
3198
+ status: "direct",
3199
+ lumiaType: "image"
3200
+ };
3201
+ if (t === "video")
3202
+ return {
3203
+ unit: mapVideo(widget, ctx),
3204
+ status: "direct",
3205
+ lumiaType: "video"
3206
+ };
2763
3207
  if (t in READOUT_TYPES) {
2764
- return { unit: mapReadout(widget, READOUT_TYPES[t], ctx), status: "direct", lumiaType: "text" };
3208
+ return {
3209
+ unit: mapReadout(widget, READOUT_TYPES[t], ctx),
3210
+ status: "direct",
3211
+ lumiaType: "text"
3212
+ };
2765
3213
  }
2766
- if (GOAL_TYPES.has(t)) return { unit: mapGoal(widget, ctx), status: "direct", lumiaType: "goal" };
3214
+ if (GOAL_TYPES.has(t))
3215
+ return { unit: mapGoal(widget, ctx), status: "direct", lumiaType: "goal" };
2767
3216
  if ((t === "se-widget-alert-box" || t === "se-widget-alert-box-merch") && hasSECustomCode(widget)) {
2768
- return { unit: mapUnsupportedAsText(widget, ctx), status: "placeholder", lumiaType: "text", flaggedOff: true };
3217
+ return {
3218
+ unit: mapUnsupportedAsText(widget, ctx),
3219
+ status: "placeholder",
3220
+ lumiaType: "text",
3221
+ flaggedOff: true
3222
+ };
2769
3223
  }
2770
- if (t === "se-widget-alert-box") return { unit: mapAlertBox(widget, {}, ctx), status: "direct", lumiaType: "alert" };
2771
- if (t === "se-widget-alert-box-merch") return { unit: mapAlertBox(widget, { onlyEvents: ["merch"] }, ctx), status: "direct", lumiaType: "alert" };
2772
- if (t === "se-widget-donor-drive-alert") return { unit: mapSingleAlert(widget, "donordrive-donation", ctx), status: "direct", lumiaType: "alert" };
2773
- if (t === "se-widget-extralife-alert") return { unit: mapSingleAlert(widget, "extralife-donation", ctx), status: "direct", lumiaType: "alert" };
2774
- if (t === "se-widget-tiltify-alert") return { unit: mapSingleAlert(widget, "tiltify-campaignDonation", ctx), status: "direct", lumiaType: "alert" };
2775
- if (EVENTLIST_TYPES.has(t)) return { unit: mapEventList(widget, ctx), status: "direct", lumiaType: "eventlist" };
2776
- if (t === "se-widget-top-tippers-list") return { unit: mapViewerProfilesLeaderboard(widget, "tips", ctx), status: "direct", lumiaType: "viewerprofiles" };
2777
- if (t === "se-widget-top-cheerers-list") return { unit: mapViewerProfilesLeaderboard(widget, "cheers", ctx), status: "direct", lumiaType: "viewerprofiles" };
2778
- if (t === "se-widget-top-gifters-list") return { unit: mapViewerProfilesLeaderboard(widget, "gifts", ctx), status: "direct", lumiaType: "viewerprofiles" };
2779
- if (t === "se-widget-credit-roll") return { unit: mapCredits(widget, ctx), status: "partial", lumiaType: "credits" };
2780
- if (t === "se-widget-twitch-chat") return { unit: mapChatbox(widget, ctx), status: "partial", lumiaType: "chatbox" };
2781
- if (t === "se-widget-image-rotator" || t === "se-widget-merch-products-rotator") return { unit: mapSlideshow(widget, ctx), status: t === "se-widget-merch-products-rotator" ? "partial" : "direct", lumiaType: "slideshow" };
2782
- if (t === "se-widget-countdown") return { unit: mapTimer(widget, ctx), status: "partial", lumiaType: "timer" };
2783
- if (t === "se-widget-current-song") return { unit: mapNowPlaying(widget, ctx), status: "partial", lumiaType: "nowplaying" };
2784
- if (t === "se-widget-kappagen") return { unit: mapKappagen(widget, ctx), status: "partial", lumiaType: "emotealert" };
2785
- if (t === "se-widget-emote-wall") return { unit: mapEmoteWall(widget, ctx), status: "partial", lumiaType: "emotebox" };
2786
- if (t === "se-widget-giveaway") return { unit: mapGiveaway(widget, ctx), status: "direct", lumiaType: "raffle" };
2787
- if (t === "se-widget-media-share") return { unit: mapMediaShare(widget, ctx), status: "partial", lumiaType: "songrequest" };
2788
- if (t === "se-widget-hype-cup") return { unit: mapHypeCup(widget, ctx), status: "partial", lumiaType: "tipjar" };
2789
- if (t === "se-widget-bit-boss") return { unit: mapStreamBoss(widget, ctx), status: "partial", lumiaType: "streamboss" };
2790
- if (t === "se-widget-hype-train") return { unit: mapHypetrain(widget, ctx), status: "partial", lumiaType: "hypetrain" };
3224
+ if (t === "se-widget-alert-box")
3225
+ return {
3226
+ unit: mapAlertBox(widget, {}, ctx),
3227
+ status: "direct",
3228
+ lumiaType: "alert"
3229
+ };
3230
+ if (t === "se-widget-alert-box-merch")
3231
+ return {
3232
+ unit: mapAlertBox(widget, { onlyEvents: ["merch"] }, ctx),
3233
+ status: "direct",
3234
+ lumiaType: "alert"
3235
+ };
3236
+ if (t === "se-widget-donor-drive-alert")
3237
+ return {
3238
+ unit: mapSingleAlert(widget, "donordrive-donation", ctx),
3239
+ status: "direct",
3240
+ lumiaType: "alert"
3241
+ };
3242
+ if (t === "se-widget-extralife-alert")
3243
+ return {
3244
+ unit: mapSingleAlert(widget, "extralife-donation", ctx),
3245
+ status: "direct",
3246
+ lumiaType: "alert"
3247
+ };
3248
+ if (t === "se-widget-tiltify-alert")
3249
+ return {
3250
+ unit: mapSingleAlert(widget, "tiltify-campaignDonation", ctx),
3251
+ status: "direct",
3252
+ lumiaType: "alert"
3253
+ };
3254
+ if (EVENTLIST_TYPES.has(t))
3255
+ return {
3256
+ unit: mapEventList(widget, ctx),
3257
+ status: "direct",
3258
+ lumiaType: "eventlist"
3259
+ };
3260
+ if (t === "se-widget-top-tippers-list")
3261
+ return {
3262
+ unit: mapViewerProfilesLeaderboard(widget, "tips", ctx),
3263
+ status: "direct",
3264
+ lumiaType: "viewerprofiles"
3265
+ };
3266
+ if (t === "se-widget-top-cheerers-list")
3267
+ return {
3268
+ unit: mapViewerProfilesLeaderboard(widget, "cheers", ctx),
3269
+ status: "direct",
3270
+ lumiaType: "viewerprofiles"
3271
+ };
3272
+ if (t === "se-widget-top-gifters-list")
3273
+ return {
3274
+ unit: mapViewerProfilesLeaderboard(widget, "gifts", ctx),
3275
+ status: "direct",
3276
+ lumiaType: "viewerprofiles"
3277
+ };
3278
+ if (t === "se-widget-credit-roll")
3279
+ return {
3280
+ unit: mapCredits(widget, ctx),
3281
+ status: "partial",
3282
+ lumiaType: "credits"
3283
+ };
3284
+ if (t === "se-widget-twitch-chat")
3285
+ return {
3286
+ unit: mapChatbox(widget, ctx),
3287
+ status: "partial",
3288
+ lumiaType: "chatbox"
3289
+ };
3290
+ if (t === "se-widget-image-rotator" || t === "se-widget-merch-products-rotator")
3291
+ return {
3292
+ unit: mapSlideshow(widget, ctx),
3293
+ status: t === "se-widget-merch-products-rotator" ? "partial" : "direct",
3294
+ lumiaType: "slideshow"
3295
+ };
3296
+ if (t === "se-widget-countdown")
3297
+ return {
3298
+ unit: mapTimer(widget, ctx),
3299
+ status: "partial",
3300
+ lumiaType: "timer"
3301
+ };
3302
+ if (t === "se-widget-current-song")
3303
+ return {
3304
+ unit: mapNowPlaying(widget, ctx),
3305
+ status: "partial",
3306
+ lumiaType: "nowplaying"
3307
+ };
3308
+ if (t === "se-widget-kappagen")
3309
+ return {
3310
+ unit: mapKappagen(widget, ctx),
3311
+ status: "partial",
3312
+ lumiaType: "emotealert"
3313
+ };
3314
+ if (t === "se-widget-emote-wall")
3315
+ return {
3316
+ unit: mapEmoteWall(widget, ctx),
3317
+ status: "partial",
3318
+ lumiaType: "emotebox"
3319
+ };
3320
+ if (t === "se-widget-giveaway")
3321
+ return {
3322
+ unit: mapGiveaway(widget, ctx),
3323
+ status: "direct",
3324
+ lumiaType: "raffle"
3325
+ };
3326
+ if (t === "se-widget-media-share")
3327
+ return {
3328
+ unit: mapMediaShare(widget, ctx),
3329
+ status: "partial",
3330
+ lumiaType: "songrequest"
3331
+ };
3332
+ if (t === "se-widget-hype-cup")
3333
+ return {
3334
+ unit: mapHypeCup(widget, ctx),
3335
+ status: "partial",
3336
+ lumiaType: "tipjar"
3337
+ };
3338
+ if (t === "se-widget-bit-boss")
3339
+ return {
3340
+ unit: mapStreamBoss(widget, ctx),
3341
+ status: "partial",
3342
+ lumiaType: "streamboss"
3343
+ };
3344
+ if (t === "se-widget-hype-train")
3345
+ return {
3346
+ unit: mapHypetrain(widget, ctx),
3347
+ status: "partial",
3348
+ lumiaType: "hypetrain"
3349
+ };
2791
3350
  if (t === "se-widget-custom-event-list" && isHypetrainCustomWidget(widget)) {
2792
- return { unit: mapHypetrain(widget, ctx), status: "partial", lumiaType: "hypetrain" };
3351
+ return {
3352
+ unit: mapHypetrain(widget, ctx),
3353
+ status: "partial",
3354
+ lumiaType: "hypetrain"
3355
+ };
2793
3356
  }
2794
- if (t === "se-widget-custom-event-list") return { unit: mapCustom(widget, ctx), status: "partial", lumiaType: "custom" };
2795
- if (t === "se-widget-snow") return { unit: mapSnow(widget, ctx), status: "direct", lumiaType: "video" };
2796
- if (t === "se-widget-halloween-2019") return { unit: mapSlideshow(widget, ctx), status: "direct", lumiaType: "slideshow" };
2797
- if (SEASONAL_IMAGE_TYPES.has(t)) return { unit: mapImage(widget, ctx), status: "direct", lumiaType: "image" };
2798
- return { unit: mapUnsupportedAsText(widget, ctx), status: "placeholder", lumiaType: "text" };
3357
+ if (t === "se-widget-custom-event-list")
3358
+ return {
3359
+ unit: mapCustom(widget, ctx),
3360
+ status: "partial",
3361
+ lumiaType: "custom"
3362
+ };
3363
+ if (t === "se-widget-snow")
3364
+ return { unit: mapSnow(widget, ctx), status: "direct", lumiaType: "video" };
3365
+ if (t === "se-widget-halloween-2019")
3366
+ return {
3367
+ unit: mapSlideshow(widget, ctx),
3368
+ status: "direct",
3369
+ lumiaType: "slideshow"
3370
+ };
3371
+ if (SEASONAL_IMAGE_TYPES.has(t))
3372
+ return {
3373
+ unit: mapImage(widget, ctx),
3374
+ status: "direct",
3375
+ lumiaType: "image"
3376
+ };
3377
+ return {
3378
+ unit: mapUnsupportedAsText(widget, ctx),
3379
+ status: "placeholder",
3380
+ lumiaType: "text"
3381
+ };
2799
3382
  }
2800
3383
  function recordCoverage(coverage, result, seType) {
2801
3384
  const existing = coverage.mappings.find((m) => m.seType === seType);
@@ -2803,7 +3386,12 @@ function recordCoverage(coverage, result, seType) {
2803
3386
  existing.count += 1;
2804
3387
  return;
2805
3388
  }
2806
- coverage.mappings.push({ seType, lumiaType: result.lumiaType, status: result.status, count: 1 });
3389
+ coverage.mappings.push({
3390
+ seType,
3391
+ lumiaType: result.lumiaType,
3392
+ status: result.status,
3393
+ count: 1
3394
+ });
2807
3395
  }
2808
3396
 
2809
3397
  // src/se-import/assets.ts
@@ -3884,12 +4472,12 @@ var LSFontPicker = memo(function LSFontPicker2({
3884
4472
  transform: "rotate(180deg)"
3885
4473
  }
3886
4474
  }
3887
- }
3888
- },
3889
- ListboxProps: {
3890
- sx: {
3891
- maxHeight: 300,
3892
- padding: 0
4475
+ },
4476
+ listbox: {
4477
+ sx: {
4478
+ maxHeight: 300,
4479
+ padding: 0
4480
+ }
3893
4481
  }
3894
4482
  },
3895
4483
  noOptionsText: "No fonts found",
@@ -3912,27 +4500,39 @@ var LSFontPicker = memo(function LSFontPicker2({
3912
4500
  key
3913
4501
  );
3914
4502
  },
3915
- renderInput: (params) => /* @__PURE__ */ jsx8(
3916
- TextFieldComponent,
3917
- {
3918
- ...params,
3919
- label,
3920
- helperText,
3921
- placeholder,
3922
- fullWidth: true,
3923
- className: "mui-ls-input",
3924
- variant: "outlined",
3925
- sx: {
3926
- "& .MuiOutlinedInput-root": {
3927
- paddingRight: "0.5rem"
4503
+ renderInput: (params) => {
4504
+ const {
4505
+ InputLabelProps: paramsInputLabelProps,
4506
+ InputProps: paramsInputProps,
4507
+ ...restParams
4508
+ } = params;
4509
+ return /* @__PURE__ */ jsx8(
4510
+ TextFieldComponent,
4511
+ {
4512
+ ...restParams,
4513
+ label,
4514
+ helperText,
4515
+ placeholder,
4516
+ fullWidth: true,
4517
+ className: "mui-ls-input",
4518
+ variant: "outlined",
4519
+ sx: {
4520
+ "& .MuiOutlinedInput-root": {
4521
+ paddingRight: "0.5rem"
4522
+ }
4523
+ },
4524
+ slotProps: {
4525
+ inputLabel: {
4526
+ shrink: true,
4527
+ ...paramsInputLabelProps ?? {}
4528
+ },
4529
+ input: {
4530
+ ...paramsInputProps ?? {}
4531
+ }
3928
4532
  }
3929
- },
3930
- InputLabelProps: {
3931
- shrink: true,
3932
- ...params.InputLabelProps ?? {}
3933
4533
  }
3934
- }
3935
- )
4534
+ );
4535
+ }
3936
4536
  }
3937
4537
  ) });
3938
4538
  });
@@ -7873,17 +8473,6 @@ function importSEBootstrap(bootstrap) {
7873
8473
  notes: []
7874
8474
  };
7875
8475
  const provider = readBootstrapProvider(bootstrap);
7876
- console.log("[SE-IMPORT] bootstrap received", {
7877
- overlayId: seOverlay?._id,
7878
- name: seOverlay?.name,
7879
- canvas: {
7880
- width: seOverlay?.settings?.width,
7881
- height: seOverlay?.settings?.height
7882
- },
7883
- widgetCount: widgets.length,
7884
- widgetTypes: widgets.map((w) => w.type),
7885
- provider
7886
- });
7887
8476
  const importCanvas = {
7888
8477
  width: seOverlay?.settings?.width ?? 1920,
7889
8478
  height: seOverlay?.settings?.height ?? 1080
@@ -8035,12 +8624,6 @@ function importSEBootstrap(bootstrap) {
8035
8624
  layers_count: layers.length,
8036
8625
  hasFullSettings: true
8037
8626
  };
8038
- console.log("[SE-IMPORT] import complete", {
8039
- overlayName: overlay.name,
8040
- layerCount: overlay.layers_count,
8041
- coverage,
8042
- reviewItemCount: reviewItems.length
8043
- });
8044
8627
  return { overlay, coverage, reviewItems };
8045
8628
  }
8046
8629
  function applyReviewAction(result, moduleId, action, payload) {