@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/index.js CHANGED
@@ -878,12 +878,12 @@ var LSFontPicker = memo(function LSFontPicker2({
878
878
  transform: "rotate(180deg)"
879
879
  }
880
880
  }
881
- }
882
- },
883
- ListboxProps: {
884
- sx: {
885
- maxHeight: 300,
886
- padding: 0
881
+ },
882
+ listbox: {
883
+ sx: {
884
+ maxHeight: 300,
885
+ padding: 0
886
+ }
887
887
  }
888
888
  },
889
889
  noOptionsText: "No fonts found",
@@ -906,27 +906,39 @@ var LSFontPicker = memo(function LSFontPicker2({
906
906
  key
907
907
  );
908
908
  },
909
- renderInput: (params) => /* @__PURE__ */ jsx8(
910
- TextFieldComponent,
911
- {
912
- ...params,
913
- label,
914
- helperText,
915
- placeholder,
916
- fullWidth: true,
917
- className: "mui-ls-input",
918
- variant: "outlined",
919
- sx: {
920
- "& .MuiOutlinedInput-root": {
921
- paddingRight: "0.5rem"
909
+ renderInput: (params) => {
910
+ const {
911
+ InputLabelProps: paramsInputLabelProps,
912
+ InputProps: paramsInputProps,
913
+ ...restParams
914
+ } = params;
915
+ return /* @__PURE__ */ jsx8(
916
+ TextFieldComponent,
917
+ {
918
+ ...restParams,
919
+ label,
920
+ helperText,
921
+ placeholder,
922
+ fullWidth: true,
923
+ className: "mui-ls-input",
924
+ variant: "outlined",
925
+ sx: {
926
+ "& .MuiOutlinedInput-root": {
927
+ paddingRight: "0.5rem"
928
+ }
929
+ },
930
+ slotProps: {
931
+ inputLabel: {
932
+ shrink: true,
933
+ ...paramsInputLabelProps ?? {}
934
+ },
935
+ input: {
936
+ ...paramsInputProps ?? {}
937
+ }
922
938
  }
923
- },
924
- InputLabelProps: {
925
- shrink: true,
926
- ...params.InputLabelProps ?? {}
927
939
  }
928
- }
929
- )
940
+ );
941
+ }
930
942
  }
931
943
  ) });
932
944
  });
@@ -3123,7 +3135,7 @@ var SE_TO_LUMIA_PLACEHOLDER = {
3123
3135
  displayName: "displayname",
3124
3136
  sender: "gifter",
3125
3137
  amount: "amount",
3126
- currency: "currency",
3138
+ currency: "currencySymbol",
3127
3139
  message: "message",
3128
3140
  items: "itemsDetailed",
3129
3141
  item: "firstItem.name",
@@ -3138,7 +3150,10 @@ var SE_TO_LUMIA_PLACEHOLDER = {
3138
3150
  streamer: "streamer"
3139
3151
  };
3140
3152
  var TWITCH_CONTEXTS = {
3141
- "follower-latest": { name: "twitch_last_follower", username: "twitch_last_follower" },
3153
+ "follower-latest": {
3154
+ name: "twitch_last_follower",
3155
+ username: "twitch_last_follower"
3156
+ },
3142
3157
  // Three SE listeners with genuinely distinct semantics — preserve the
3143
3158
  // distinction in the variable mapping so a widget configured for "Latest
3144
3159
  // new subscriber" doesn't render the latest *resub* (and vice versa).
@@ -3146,9 +3161,18 @@ var TWITCH_CONTEXTS = {
3146
3161
  // are subtype-specific. The dedicated TWITCH_LAST_{NEW_SUBSCRIBER,
3147
3162
  // RESUBSCRIBER} variables are written by the Twitch event pipeline on
3148
3163
  // the matching sub.type event.
3149
- "subscriber-latest": { name: "twitch_last_subscriber", username: "twitch_last_subscriber" },
3150
- "subscriber-new-latest": { name: "twitch_last_new_subscriber", username: "twitch_last_new_subscriber" },
3151
- "subscriber-resub-latest": { name: "twitch_last_resubscriber", username: "twitch_last_resubscriber" },
3164
+ "subscriber-latest": {
3165
+ name: "twitch_last_subscriber",
3166
+ username: "twitch_last_subscriber"
3167
+ },
3168
+ "subscriber-new-latest": {
3169
+ name: "twitch_last_new_subscriber",
3170
+ username: "twitch_last_new_subscriber"
3171
+ },
3172
+ "subscriber-resub-latest": {
3173
+ name: "twitch_last_resubscriber",
3174
+ username: "twitch_last_resubscriber"
3175
+ },
3152
3176
  // `subscriber-gifted-latest`: SE uses `{sender}` for the giver and `{name}` for
3153
3177
  // the recipient (e.g. "{sender} gifted to {name}"). The two roles must point
3154
3178
  // at distinct Lumia variables — collapsing both onto `twitch_last_gifter`
@@ -3156,12 +3180,45 @@ var TWITCH_CONTEXTS = {
3156
3180
  // nowhere. `sender` keeps the gifter; `name` / `username` switch to the
3157
3181
  // dedicated recipient variable `twitch_last_gifted`. Amount stays on the
3158
3182
  // gifter (it's the count of subs in that drop, owned by the gifter).
3159
- "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" },
3160
- "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" },
3161
- "cheer-latest": { name: "twitch_last_bit", username: "twitch_last_bit", amount: "twitch_last_bit_amount", count: "twitch_last_bit_amount" },
3162
- "tip-latest": { name: "latest_donator", username: "latest_donator", amount: "latest_donator_amount", count: "latest_donator_amount", currency: "latest_donator_currency_symbol" },
3163
- latest_donator: { name: "latest_donator", username: "latest_donator", amount: "latest_donator_amount", count: "latest_donator_amount", currency: "latest_donator_currency_symbol" },
3164
- "raid-latest": { name: "twitch_last_raider", username: "twitch_last_raider", amount: "twitch_last_raid_amount", count: "twitch_last_raid_amount" },
3183
+ "subscriber-gifted-latest": {
3184
+ name: "twitch_last_gifted",
3185
+ username: "twitch_last_gifted",
3186
+ sender: "twitch_last_gifter",
3187
+ amount: "twitch_last_gifter_amount",
3188
+ count: "twitch_last_gifter_amount"
3189
+ },
3190
+ "subscriber-alltime-gifter": {
3191
+ name: "twitch_alltime_top_gifter",
3192
+ username: "twitch_alltime_top_gifter",
3193
+ amount: "twitch_alltime_top_gifter_amount",
3194
+ count: "twitch_alltime_top_gifter_amount"
3195
+ },
3196
+ "cheer-latest": {
3197
+ name: "twitch_last_bit",
3198
+ username: "twitch_last_bit",
3199
+ amount: "twitch_last_bit_amount",
3200
+ count: "twitch_last_bit_amount"
3201
+ },
3202
+ "tip-latest": {
3203
+ name: "latest_donator",
3204
+ username: "latest_donator",
3205
+ amount: "latest_donator_amount",
3206
+ count: "latest_donator_amount",
3207
+ currency: "latest_donator_currency_symbol"
3208
+ },
3209
+ latest_donator: {
3210
+ name: "latest_donator",
3211
+ username: "latest_donator",
3212
+ amount: "latest_donator_amount",
3213
+ count: "latest_donator_amount",
3214
+ currency: "latest_donator_currency_symbol"
3215
+ },
3216
+ "raid-latest": {
3217
+ name: "twitch_last_raider",
3218
+ username: "twitch_last_raider",
3219
+ amount: "twitch_last_raid_amount",
3220
+ count: "twitch_last_raid_amount"
3221
+ },
3165
3222
  // Period counters: count/amount = the running counter for that window.
3166
3223
  // SE lets the streamer pick session / week / month / total on the same widget
3167
3224
  // type; the chosen period lands in `widget.listener` (e.g. `cheer-week`).
@@ -3169,25 +3226,86 @@ var TWITCH_CONTEXTS = {
3169
3226
  // table only carried session+total and broke any widget configured for
3170
3227
  // week/month because `{amount}` fell through to the unscoped placeholder
3171
3228
  // and rendered blank.
3172
- "follower-session": { amount: "twitch_session_follower_count", count: "twitch_session_follower_count" },
3173
- "follower-week": { amount: "twitch_week_follower_count", count: "twitch_week_follower_count" },
3174
- "follower-month": { amount: "twitch_month_follower_count", count: "twitch_month_follower_count" },
3175
- "follower-total": { amount: "twitch_total_follower_count", count: "twitch_total_follower_count" },
3176
- "subscriber-session": { amount: "twitch_session_subscribers_count", count: "twitch_session_subscribers_count" },
3177
- "subscriber-week": { amount: "twitch_week_subscriber_count", count: "twitch_week_subscriber_count" },
3178
- "subscriber-month": { amount: "twitch_month_subscriber_count", count: "twitch_month_subscriber_count" },
3179
- "subscriber-total": { amount: "twitch_total_subscriber_count", count: "twitch_total_subscriber_count" },
3180
- "subscriber-new-session": { amount: "twitch_session_new_subscribers_count", count: "twitch_session_new_subscribers_count" },
3181
- "subscriber-resub-session": { amount: "twitch_session_resub_subscribers_count", count: "twitch_session_resub_subscribers_count" },
3182
- "subscriber-gifted-session": { amount: "twitch_session_gifted_subscribers_count", count: "twitch_session_gifted_subscribers_count" },
3183
- "cheer-session": { amount: "twitch_session_bits_count", count: "twitch_session_bits_count" },
3184
- "cheer-week": { amount: "twitch_week_bits_count", count: "twitch_week_bits_count" },
3185
- "cheer-month": { amount: "twitch_month_bits_count", count: "twitch_month_bits_count" },
3186
- "cheer-total": { amount: "twitch_total_bits_count", count: "twitch_total_bits_count" },
3187
- "tip-session": { amount: "session_donation_amount", count: "session_donation_count", currency: "session_donation_amount_currency_symbol" },
3188
- "tip-week": { amount: "week_donation_amount", count: "week_donation_count", currency: "week_donation_amount_currency_symbol" },
3189
- "tip-month": { amount: "month_donation_amount", count: "month_donation_count", currency: "month_donation_amount_currency_symbol" },
3190
- "tip-total": { amount: "total_donation_amount", count: "total_donation_amount", currency: "total_donation_amount_currency_symbol" },
3229
+ "follower-session": {
3230
+ amount: "twitch_session_follower_count",
3231
+ count: "twitch_session_follower_count"
3232
+ },
3233
+ "follower-week": {
3234
+ amount: "twitch_week_follower_count",
3235
+ count: "twitch_week_follower_count"
3236
+ },
3237
+ "follower-month": {
3238
+ amount: "twitch_month_follower_count",
3239
+ count: "twitch_month_follower_count"
3240
+ },
3241
+ "follower-total": {
3242
+ amount: "twitch_total_follower_count",
3243
+ count: "twitch_total_follower_count"
3244
+ },
3245
+ "subscriber-session": {
3246
+ amount: "twitch_session_subscribers_count",
3247
+ count: "twitch_session_subscribers_count"
3248
+ },
3249
+ "subscriber-week": {
3250
+ amount: "twitch_week_subscriber_count",
3251
+ count: "twitch_week_subscriber_count"
3252
+ },
3253
+ "subscriber-month": {
3254
+ amount: "twitch_month_subscriber_count",
3255
+ count: "twitch_month_subscriber_count"
3256
+ },
3257
+ "subscriber-total": {
3258
+ amount: "twitch_total_subscriber_count",
3259
+ count: "twitch_total_subscriber_count"
3260
+ },
3261
+ "subscriber-new-session": {
3262
+ amount: "twitch_session_new_subscribers_count",
3263
+ count: "twitch_session_new_subscribers_count"
3264
+ },
3265
+ "subscriber-resub-session": {
3266
+ amount: "twitch_session_resub_subscribers_count",
3267
+ count: "twitch_session_resub_subscribers_count"
3268
+ },
3269
+ "subscriber-gifted-session": {
3270
+ amount: "twitch_session_gifted_subscribers_count",
3271
+ count: "twitch_session_gifted_subscribers_count"
3272
+ },
3273
+ "cheer-session": {
3274
+ amount: "twitch_session_bits_count",
3275
+ count: "twitch_session_bits_count"
3276
+ },
3277
+ "cheer-week": {
3278
+ amount: "twitch_week_bits_count",
3279
+ count: "twitch_week_bits_count"
3280
+ },
3281
+ "cheer-month": {
3282
+ amount: "twitch_month_bits_count",
3283
+ count: "twitch_month_bits_count"
3284
+ },
3285
+ "cheer-total": {
3286
+ amount: "twitch_total_bits_count",
3287
+ count: "twitch_total_bits_count"
3288
+ },
3289
+ "tip-session": {
3290
+ amount: "session_donation_amount",
3291
+ count: "session_donation_count",
3292
+ currency: "session_donation_amount_currency_symbol"
3293
+ },
3294
+ "tip-week": {
3295
+ amount: "week_donation_amount",
3296
+ count: "week_donation_count",
3297
+ currency: "week_donation_amount_currency_symbol"
3298
+ },
3299
+ "tip-month": {
3300
+ amount: "month_donation_amount",
3301
+ count: "month_donation_count",
3302
+ currency: "month_donation_amount_currency_symbol"
3303
+ },
3304
+ "tip-total": {
3305
+ amount: "total_donation_amount",
3306
+ count: "total_donation_amount",
3307
+ currency: "total_donation_amount_currency_symbol"
3308
+ },
3191
3309
  "tip-count": { amount: "donation_count", count: "donation_count" },
3192
3310
  // Top cheer / cheerer / donator readouts — per period. SE's `se-widget-top-*`
3193
3311
  // widgets encode the chosen period (session / weekly / monthly / alltime) in
@@ -3203,26 +3321,118 @@ var TWITCH_CONTEXTS = {
3203
3321
  // "the user who made the top donation", so the two rows below carry the
3204
3322
  // same name/username mapping — anything else falls through to the unscoped
3205
3323
  // `{{username}}` and renders blank.
3206
- "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" },
3207
- "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" },
3208
- "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" },
3209
- "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" },
3210
- "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" },
3211
- "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" },
3212
- "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" },
3213
- "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" },
3214
- "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" },
3215
- "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" },
3216
- "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" },
3217
- "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" },
3218
- "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" },
3219
- "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" },
3220
- "tip-alltime-top-donation": { name: "top_donator", username: "top_donator", amount: "top_donator_amount", count: "top_donator_amount", currency: "top_donator_currency_symbol" },
3221
- "tip-alltime-top-donator": { name: "top_donator", username: "top_donator", amount: "top_donator_amount", count: "top_donator_amount", currency: "top_donator_currency_symbol" },
3324
+ "cheer-session-top-donation": {
3325
+ name: "twitch_session_top_cheerer",
3326
+ username: "twitch_session_top_cheerer",
3327
+ amount: "twitch_session_top_cheer_amount",
3328
+ count: "twitch_session_top_cheer_amount"
3329
+ },
3330
+ "cheer-session-top-donator": {
3331
+ name: "twitch_session_top_cheerer",
3332
+ username: "twitch_session_top_cheerer",
3333
+ amount: "twitch_session_top_cheerer_amount",
3334
+ count: "twitch_session_top_cheerer_amount"
3335
+ },
3336
+ "cheer-weekly-top-donation": {
3337
+ name: "twitch_week_top_cheerer",
3338
+ username: "twitch_week_top_cheerer",
3339
+ amount: "twitch_week_top_cheerer_amount",
3340
+ count: "twitch_week_top_cheerer_amount"
3341
+ },
3342
+ "cheer-weekly-top-donator": {
3343
+ name: "twitch_week_top_cheerer",
3344
+ username: "twitch_week_top_cheerer",
3345
+ amount: "twitch_week_top_cheerer_amount",
3346
+ count: "twitch_week_top_cheerer_amount"
3347
+ },
3348
+ "cheer-monthly-top-donation": {
3349
+ name: "twitch_month_top_cheerer",
3350
+ username: "twitch_month_top_cheerer",
3351
+ amount: "twitch_month_top_cheerer_amount",
3352
+ count: "twitch_month_top_cheerer_amount"
3353
+ },
3354
+ "cheer-monthly-top-donator": {
3355
+ name: "twitch_month_top_cheerer",
3356
+ username: "twitch_month_top_cheerer",
3357
+ amount: "twitch_month_top_cheerer_amount",
3358
+ count: "twitch_month_top_cheerer_amount"
3359
+ },
3360
+ "cheer-alltime-top-donation": {
3361
+ name: "twitch_alltime_top_cheerer",
3362
+ username: "twitch_alltime_top_cheerer",
3363
+ amount: "twitch_alltime_top_cheerer_amount",
3364
+ count: "twitch_alltime_top_cheerer_amount"
3365
+ },
3366
+ "cheer-alltime-top-donator": {
3367
+ name: "twitch_alltime_top_cheerer",
3368
+ username: "twitch_alltime_top_cheerer",
3369
+ amount: "twitch_alltime_top_cheerer_amount",
3370
+ count: "twitch_alltime_top_cheerer_amount"
3371
+ },
3372
+ "tip-session-top-donation": {
3373
+ name: "session_top_donator",
3374
+ username: "session_top_donator",
3375
+ amount: "session_top_donator_amount",
3376
+ count: "session_top_donator_amount",
3377
+ currency: "session_top_donator_currency_symbol"
3378
+ },
3379
+ "tip-session-top-donator": {
3380
+ name: "session_top_donator",
3381
+ username: "session_top_donator",
3382
+ amount: "session_top_donator_amount",
3383
+ count: "session_top_donator_amount",
3384
+ currency: "session_top_donator_currency_symbol"
3385
+ },
3386
+ "tip-weekly-top-donation": {
3387
+ name: "week_top_donator",
3388
+ username: "week_top_donator",
3389
+ amount: "week_top_donator_amount",
3390
+ count: "week_top_donator_amount",
3391
+ currency: "week_top_donator_currency_symbol"
3392
+ },
3393
+ "tip-weekly-top-donator": {
3394
+ name: "week_top_donator",
3395
+ username: "week_top_donator",
3396
+ amount: "week_top_donator_amount",
3397
+ count: "week_top_donator_amount",
3398
+ currency: "week_top_donator_currency_symbol"
3399
+ },
3400
+ "tip-monthly-top-donation": {
3401
+ name: "month_top_donator",
3402
+ username: "month_top_donator",
3403
+ amount: "month_top_donator_amount",
3404
+ count: "month_top_donator_amount",
3405
+ currency: "month_top_donator_currency_symbol"
3406
+ },
3407
+ "tip-monthly-top-donator": {
3408
+ name: "month_top_donator",
3409
+ username: "month_top_donator",
3410
+ amount: "month_top_donator_amount",
3411
+ count: "month_top_donator_amount",
3412
+ currency: "month_top_donator_currency_symbol"
3413
+ },
3414
+ "tip-alltime-top-donation": {
3415
+ name: "top_donator",
3416
+ username: "top_donator",
3417
+ amount: "top_donator_amount",
3418
+ count: "top_donator_amount",
3419
+ currency: "top_donator_currency_symbol"
3420
+ },
3421
+ "tip-alltime-top-donator": {
3422
+ name: "top_donator",
3423
+ username: "top_donator",
3424
+ amount: "top_donator_amount",
3425
+ count: "top_donator_amount",
3426
+ currency: "top_donator_currency_symbol"
3427
+ },
3222
3428
  // Botcounter — SE uses `{wins}` for the running chat count; the dispatcher's
3223
3429
  // fallbackVar for `se-widget-botcounter` is `twitch_session_chat_count`, so
3224
3430
  // when the listener arg lands as that var name we treat it as the wins source.
3225
- twitch_session_chat_count: { wins: "twitch_session_chat_count", count: "twitch_session_chat_count", amount: "twitch_session_chat_count" },
3431
+ twitch_session_chat_count: {
3432
+ wins: "twitch_session_chat_count",
3433
+ count: "twitch_session_chat_count",
3434
+ amount: "twitch_session_chat_count"
3435
+ },
3226
3436
  // Merch readouts — SE used to ship via their own shop, now most users route
3227
3437
  // through Fourthwall. Lumia exposes `fourthwall_last_order` as a full object:
3228
3438
  // { username, email, friendlyId, amount, currency, message, items,
@@ -3251,45 +3461,151 @@ var TWITCH_CONTEXTS = {
3251
3461
  }
3252
3462
  };
3253
3463
  var KICK_CONTEXTS = {
3254
- "follower-latest": { name: "kick_last_follower", username: "kick_last_follower" },
3255
- "subscriber-latest": { name: "kick_last_subscriber", username: "kick_last_subscriber" },
3256
- "subscriber-new-latest": { name: "kick_last_subscriber", username: "kick_last_subscriber" },
3257
- "subscriber-resub-latest": { name: "kick_last_subscriber", username: "kick_last_subscriber" },
3464
+ "follower-latest": {
3465
+ name: "kick_last_follower",
3466
+ username: "kick_last_follower"
3467
+ },
3468
+ "subscriber-latest": {
3469
+ name: "kick_last_subscriber",
3470
+ username: "kick_last_subscriber"
3471
+ },
3472
+ "subscriber-new-latest": {
3473
+ name: "kick_last_subscriber",
3474
+ username: "kick_last_subscriber"
3475
+ },
3476
+ "subscriber-resub-latest": {
3477
+ name: "kick_last_subscriber",
3478
+ username: "kick_last_subscriber"
3479
+ },
3258
3480
  // Mirror the Twitch split: `{sender}` = giver, `{name}` = recipient.
3259
3481
  // `kick_last_gifted` was added alongside this mapping; see twitch context
3260
3482
  // above for the rationale (without the split, SE's "{sender} gifted to
3261
3483
  // {name}" renders the gifter on both sides).
3262
- "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" },
3263
- "cheer-latest": { name: "kick_last_kicks", username: "kick_last_kicks", amount: "kick_last_kicks_amount", count: "kick_last_kicks_amount" },
3264
- "raid-latest": { name: "kick_last_host", username: "kick_last_host", amount: "kick_last_host_amount", count: "kick_last_host_amount" },
3265
- "follower-session": { amount: "kick_session_follower_count", count: "kick_session_follower_count" },
3266
- "subscriber-session": { amount: "kick_session_subscriber_count", count: "kick_session_subscriber_count" },
3267
- "cheer-session": { amount: "kick_session_kicks_count", count: "kick_session_kicks_count" },
3268
- "subscriber-gifted-session": { amount: "kick_session_gifts_count", count: "kick_session_gifts_count" },
3269
- "follower-total": { amount: "kick_total_follower_count", count: "kick_total_follower_count" },
3270
- "subscriber-total": { amount: "kick_total_subscriber_count", count: "kick_total_subscriber_count" },
3271
- "cheer-total": { amount: "kick_total_kicks_count", count: "kick_total_kicks_count" },
3272
- "follower-week": { amount: "kick_week_follower_count", count: "kick_week_follower_count" },
3273
- "follower-month": { amount: "kick_month_follower_count", count: "kick_month_follower_count" },
3274
- "subscriber-week": { amount: "kick_week_subscriber_count", count: "kick_week_subscriber_count" },
3275
- "subscriber-month": { amount: "kick_month_subscriber_count", count: "kick_month_subscriber_count" }
3484
+ "subscriber-gifted-latest": {
3485
+ name: "kick_last_gifted",
3486
+ username: "kick_last_gifted",
3487
+ sender: "kick_last_gifter",
3488
+ amount: "kick_last_gifter_amount",
3489
+ count: "kick_last_gifter_amount"
3490
+ },
3491
+ "cheer-latest": {
3492
+ name: "kick_last_kicks",
3493
+ username: "kick_last_kicks",
3494
+ amount: "kick_last_kicks_amount",
3495
+ count: "kick_last_kicks_amount"
3496
+ },
3497
+ "raid-latest": {
3498
+ name: "kick_last_host",
3499
+ username: "kick_last_host",
3500
+ amount: "kick_last_host_amount",
3501
+ count: "kick_last_host_amount"
3502
+ },
3503
+ "follower-session": {
3504
+ amount: "kick_session_follower_count",
3505
+ count: "kick_session_follower_count"
3506
+ },
3507
+ "subscriber-session": {
3508
+ amount: "kick_session_subscriber_count",
3509
+ count: "kick_session_subscriber_count"
3510
+ },
3511
+ "cheer-session": {
3512
+ amount: "kick_session_kicks_count",
3513
+ count: "kick_session_kicks_count"
3514
+ },
3515
+ "subscriber-gifted-session": {
3516
+ amount: "kick_session_gifts_count",
3517
+ count: "kick_session_gifts_count"
3518
+ },
3519
+ "follower-total": {
3520
+ amount: "kick_total_follower_count",
3521
+ count: "kick_total_follower_count"
3522
+ },
3523
+ "subscriber-total": {
3524
+ amount: "kick_total_subscriber_count",
3525
+ count: "kick_total_subscriber_count"
3526
+ },
3527
+ "cheer-total": {
3528
+ amount: "kick_total_kicks_count",
3529
+ count: "kick_total_kicks_count"
3530
+ },
3531
+ "follower-week": {
3532
+ amount: "kick_week_follower_count",
3533
+ count: "kick_week_follower_count"
3534
+ },
3535
+ "follower-month": {
3536
+ amount: "kick_month_follower_count",
3537
+ count: "kick_month_follower_count"
3538
+ },
3539
+ "subscriber-week": {
3540
+ amount: "kick_week_subscriber_count",
3541
+ count: "kick_week_subscriber_count"
3542
+ },
3543
+ "subscriber-month": {
3544
+ amount: "kick_month_subscriber_count",
3545
+ count: "kick_month_subscriber_count"
3546
+ }
3276
3547
  };
3277
3548
  var YOUTUBE_CONTEXTS = {
3278
- "follower-latest": { name: "youtube_last_subscriber", username: "youtube_last_subscriber" },
3279
- "subscriber-latest": { name: "youtube_last_member", username: "youtube_last_member" },
3280
- "subscriber-new-latest": { name: "youtube_last_member", username: "youtube_last_member" },
3281
- "subscriber-resub-latest": { name: "youtube_last_member", username: "youtube_last_member" },
3282
- "subscriber-gifted-latest": { name: "youtube_last_gift_member", username: "youtube_last_gift_member" },
3283
- "cheer-latest": { name: "youtube_last_superchatter", username: "youtube_last_superchatter" },
3284
- "follower-session": { amount: "youtube_session_subscriber_count", count: "youtube_session_subscriber_count" },
3285
- "subscriber-session": { amount: "youtube_session_member_count", count: "youtube_session_member_count" },
3286
- "cheer-session": { amount: "youtube_session_superchat_count", count: "youtube_session_superchat_count" },
3287
- "follower-total": { amount: "youtube_total_subscriber_count", count: "youtube_total_subscriber_count" },
3288
- "subscriber-total": { amount: "youtube_total_member_count", count: "youtube_total_member_count" },
3289
- "follower-week": { amount: "youtube_week_subscriber_count", count: "youtube_week_subscriber_count" },
3290
- "follower-month": { amount: "youtube_month_subscriber_count", count: "youtube_month_subscriber_count" },
3291
- "subscriber-week": { amount: "youtube_week_member_count", count: "youtube_week_member_count" },
3292
- "subscriber-month": { amount: "youtube_month_member_count", count: "youtube_month_member_count" }
3549
+ "follower-latest": {
3550
+ name: "youtube_last_subscriber",
3551
+ username: "youtube_last_subscriber"
3552
+ },
3553
+ "subscriber-latest": {
3554
+ name: "youtube_last_member",
3555
+ username: "youtube_last_member"
3556
+ },
3557
+ "subscriber-new-latest": {
3558
+ name: "youtube_last_member",
3559
+ username: "youtube_last_member"
3560
+ },
3561
+ "subscriber-resub-latest": {
3562
+ name: "youtube_last_member",
3563
+ username: "youtube_last_member"
3564
+ },
3565
+ "subscriber-gifted-latest": {
3566
+ name: "youtube_last_gift_member",
3567
+ username: "youtube_last_gift_member"
3568
+ },
3569
+ "cheer-latest": {
3570
+ name: "youtube_last_superchatter",
3571
+ username: "youtube_last_superchatter"
3572
+ },
3573
+ "follower-session": {
3574
+ amount: "youtube_session_subscriber_count",
3575
+ count: "youtube_session_subscriber_count"
3576
+ },
3577
+ "subscriber-session": {
3578
+ amount: "youtube_session_member_count",
3579
+ count: "youtube_session_member_count"
3580
+ },
3581
+ "cheer-session": {
3582
+ amount: "youtube_session_superchat_count",
3583
+ count: "youtube_session_superchat_count"
3584
+ },
3585
+ "follower-total": {
3586
+ amount: "youtube_total_subscriber_count",
3587
+ count: "youtube_total_subscriber_count"
3588
+ },
3589
+ "subscriber-total": {
3590
+ amount: "youtube_total_member_count",
3591
+ count: "youtube_total_member_count"
3592
+ },
3593
+ "follower-week": {
3594
+ amount: "youtube_week_subscriber_count",
3595
+ count: "youtube_week_subscriber_count"
3596
+ },
3597
+ "follower-month": {
3598
+ amount: "youtube_month_subscriber_count",
3599
+ count: "youtube_month_subscriber_count"
3600
+ },
3601
+ "subscriber-week": {
3602
+ amount: "youtube_week_member_count",
3603
+ count: "youtube_week_member_count"
3604
+ },
3605
+ "subscriber-month": {
3606
+ amount: "youtube_month_member_count",
3607
+ count: "youtube_month_member_count"
3608
+ }
3293
3609
  };
3294
3610
  var RECENT_TO_LATEST_LISTENER = {
3295
3611
  "follower-recent": "follower-latest",
@@ -3341,11 +3657,14 @@ function translateSeText(input, listener, passthrough, provider, options) {
3341
3657
  if (!input) return "";
3342
3658
  const ctx = listener ? listenerPlaceholderContext(listener, provider) : {};
3343
3659
  const useAlertFallback = (options?.context ?? "alert") === "alert";
3344
- return input.replace(/\{([a-zA-Z][a-zA-Z0-9_.]*)\}/g, (_match, raw) => {
3345
- if (passthrough?.has(raw)) return `{{${raw}}}`;
3346
- const mapped = ctx[raw] ?? (useAlertFallback ? SE_TO_LUMIA_PLACEHOLDER[raw] : void 0) ?? raw;
3347
- return `{{${mapped}}}`;
3348
- });
3660
+ return input.replace(
3661
+ /\{([a-zA-Z][a-zA-Z0-9_.]*)\}/g,
3662
+ (_match, raw) => {
3663
+ if (passthrough?.has(raw)) return `{{${raw}}}`;
3664
+ const mapped = ctx[raw] ?? (useAlertFallback ? SE_TO_LUMIA_PLACEHOLDER[raw] : void 0) ?? raw;
3665
+ return `{{${mapped}}}`;
3666
+ }
3667
+ );
3349
3668
  }
3350
3669
  var KICK_LISTENER_VARS = {
3351
3670
  "follower-latest": "kick_last_follower",
@@ -3388,8 +3707,10 @@ var YOUTUBE_LISTENER_VARS = {
3388
3707
  function listenerToLumiaVariable(listener, provider = "twitch") {
3389
3708
  if (typeof listener !== "string" || !listener) return "username";
3390
3709
  const resolvedListener = RECENT_TO_LATEST_LISTENER[listener] ?? listener;
3391
- if (provider === "kick" && KICK_LISTENER_VARS[resolvedListener]) return KICK_LISTENER_VARS[resolvedListener];
3392
- if (provider === "youtube" && YOUTUBE_LISTENER_VARS[resolvedListener]) return YOUTUBE_LISTENER_VARS[resolvedListener];
3710
+ if (provider === "kick" && KICK_LISTENER_VARS[resolvedListener])
3711
+ return KICK_LISTENER_VARS[resolvedListener];
3712
+ if (provider === "youtube" && YOUTUBE_LISTENER_VARS[resolvedListener])
3713
+ return YOUTUBE_LISTENER_VARS[resolvedListener];
3393
3714
  const m = {
3394
3715
  // Latest events
3395
3716
  "follower-latest": "twitch_last_follower",
@@ -3482,7 +3803,9 @@ function seCssToBounds(css, defaults = { width: 600, height: 200 }, canvas) {
3482
3803
  let x = toPx(css.left, 0, "width");
3483
3804
  let y = toPx(css.top, 0, "height");
3484
3805
  if (typeof css.transform === "string" && css.transform.includes("translate")) {
3485
- const m = /translate\s*\(\s*([^,)]+)\s*(?:,\s*([^)]+))?\s*\)/i.exec(css.transform);
3806
+ const m = /translate\s*\(\s*([^,)]+)\s*(?:,\s*([^)]+))?\s*\)/i.exec(
3807
+ css.transform
3808
+ );
3486
3809
  if (m) {
3487
3810
  const parseShift = (raw, basis) => {
3488
3811
  if (!raw) return 0;
@@ -3552,21 +3875,33 @@ var LUMIA_DEFAULT_SIZES = {
3552
3875
  };
3553
3876
  function translateVariationCondition(seType, seCondition, requirement, seEventType) {
3554
3877
  if (seType === "gift") return { conditionType: "IS_GIFT", condition: 1 };
3555
- if (seType === "communityGift") return { conditionType: "GIFT_SUB_GREATER", condition: requirement ?? 0 };
3878
+ if (seType === "communityGift")
3879
+ return { conditionType: "GIFT_SUB_GREATER", condition: requirement ?? 0 };
3556
3880
  const isSubscriberEvent = seEventType === "subscriber";
3557
3881
  if (isSubscriberEvent && seCondition === "ATLEAST" && requirement != null) {
3558
- return { conditionType: "SUBSCRIBED_MONTHS_GREATER", condition: requirement };
3882
+ return {
3883
+ conditionType: "SUBSCRIBED_MONTHS_GREATER",
3884
+ condition: requirement
3885
+ };
3559
3886
  }
3560
3887
  if (isSubscriberEvent && seCondition === "EXACT" && requirement != null) {
3561
3888
  return { conditionType: "SUBSCRIBED_MONTHS_EQUAL", condition: requirement };
3562
3889
  }
3563
- if (seCondition === "EXACT" && requirement != null) return { conditionType: "EQUAL_NUMBER", condition: requirement };
3890
+ if (seCondition === "EXACT" && requirement != null)
3891
+ return { conditionType: "EQUAL_NUMBER", condition: requirement };
3564
3892
  if (seCondition === "ATLEAST" && requirement != null) {
3565
3893
  return { conditionType: "GREATER_NUMBER", condition: requirement };
3566
3894
  }
3567
3895
  return { conditionType: "RANDOM", condition: 100 };
3568
3896
  }
3569
- var LUMIA_LAYOUTS = /* @__PURE__ */ new Set(["column", "row", "onlyImage", "onlyText", "imageOver", "textOver"]);
3897
+ var LUMIA_LAYOUTS = /* @__PURE__ */ new Set([
3898
+ "column",
3899
+ "row",
3900
+ "onlyImage",
3901
+ "onlyText",
3902
+ "imageOver",
3903
+ "textOver"
3904
+ ]);
3570
3905
  var SE_LAYOUT_ALIASES = {
3571
3906
  behind: "textOver"
3572
3907
  };
@@ -3593,13 +3928,27 @@ function mapSeTextCssToModuleCss(widget, defaults = {}) {
3593
3928
  return fallback;
3594
3929
  };
3595
3930
  return {
3596
- lineHeight: asNumOrStr(seTextCss["line-height"], defaults.lineHeight ?? 1),
3931
+ lineHeight: asNumOrStr(
3932
+ seTextCss["line-height"],
3933
+ defaults.lineHeight ?? "normal"
3934
+ ),
3597
3935
  fontSize: asNumOrStr(seTextCss["font-size"], defaults.fontSize ?? 24),
3598
3936
  textAlign: asStr(seTextCss["text-align"], defaults.textAlign ?? "center"),
3599
- fontFamily: asStr(seTextCss["font-family"], defaults.fontFamily ?? "Roboto"),
3937
+ fontFamily: asStr(
3938
+ seTextCss["font-family"],
3939
+ defaults.fontFamily ?? "Roboto"
3940
+ ),
3600
3941
  fontWeight: asStr(seTextCss["font-weight"], defaults.fontWeight ?? "bold"),
3942
+ fontStyle: asStr(seTextCss["font-style"], defaults.fontStyle ?? "normal"),
3943
+ textTransform: asStr(
3944
+ seTextCss["text-transform"],
3945
+ defaults.textTransform ?? "none"
3946
+ ),
3601
3947
  color: asStr(seTextCss.color, defaults.color ?? "#ffffff"),
3602
- textShadow: asStr(seTextCss["text-shadow"], defaults.textShadow ?? "rgb(0, 0, 0) 1px 1px 1px"),
3948
+ textShadow: asStr(
3949
+ seTextCss["text-shadow"],
3950
+ defaults.textShadow ?? "rgb(0, 0, 0) 1px 1px 1px"
3951
+ ),
3603
3952
  background: defaults.background ?? "transparent"
3604
3953
  };
3605
3954
  }
@@ -3648,10 +3997,16 @@ function buildUnit(widget, lumiaType, moduleExtras, ctx, sizeOverride) {
3648
3997
  // src/se-import/mappers/basic.ts
3649
3998
  function seExtraTextCss(seCss, scrolling) {
3650
3999
  const extra = {};
3651
- if (typeof seCss["text-decoration"] === "string") extra.textDecoration = seCss["text-decoration"];
3652
- if (typeof seCss["text-transform"] === "string") extra.textTransform = seCss["text-transform"];
3653
- if (seCss["-webkit-text-stroke-color"]) extra["-webkit-text-stroke-color"] = seCss["-webkit-text-stroke-color"];
3654
- if (seCss["-webkit-text-stroke-width"] != null) extra["-webkit-text-stroke-width"] = seCss["-webkit-text-stroke-width"];
4000
+ if (typeof seCss["text-decoration"] === "string")
4001
+ extra.textDecoration = seCss["text-decoration"];
4002
+ if (typeof seCss["text-transform"] === "string")
4003
+ extra.textTransform = seCss["text-transform"];
4004
+ if (typeof seCss["font-style"] === "string")
4005
+ extra.fontStyle = seCss["font-style"];
4006
+ if (seCss["-webkit-text-stroke-color"])
4007
+ extra["-webkit-text-stroke-color"] = seCss["-webkit-text-stroke-color"];
4008
+ if (seCss["-webkit-text-stroke-width"] != null)
4009
+ extra["-webkit-text-stroke-width"] = seCss["-webkit-text-stroke-width"];
3655
4010
  if (scrolling?.enabled) {
3656
4011
  extra.scroll = true;
3657
4012
  const speed = Number(scrolling.speed);
@@ -3660,54 +4015,82 @@ function seExtraTextCss(seCss, scrolling) {
3660
4015
  return extra;
3661
4016
  }
3662
4017
  function mapText(widget, ctx) {
3663
- const value = translateSeText(widget.text?.value ?? "", null, void 0, ctx?.provider, { context: "static" });
4018
+ const value = translateSeText(
4019
+ widget.text?.value ?? "",
4020
+ null,
4021
+ void 0,
4022
+ ctx?.provider,
4023
+ { context: "static" }
4024
+ );
3664
4025
  const seCss = widget.text?.css ?? {};
3665
- return buildUnit(widget, "text", {
3666
- content: {
3667
- value,
3668
- highlightColor: "inherit"
4026
+ return buildUnit(
4027
+ widget,
4028
+ "text",
4029
+ {
4030
+ content: {
4031
+ value,
4032
+ highlightColor: "inherit"
4033
+ },
4034
+ css: {
4035
+ fontSize: seCss["font-size"] ?? 24,
4036
+ textAlign: seCss["text-align"] ?? "center",
4037
+ fontFamily: seCss["font-family"] ?? "Roboto",
4038
+ fontWeight: seCss["font-weight"] ?? "bold",
4039
+ color: seCss["color"] ?? "#ffffff",
4040
+ textShadow: seCss["text-shadow"] ?? "rgb(0, 0, 0) 1px 1px 1px",
4041
+ lineHeight: seCss["line-height"] ?? "normal",
4042
+ background: "transparent",
4043
+ ...seExtraTextCss(seCss, widget.text?.scrolling)
4044
+ }
3669
4045
  },
3670
- css: {
3671
- fontSize: seCss["font-size"] ?? 24,
3672
- textAlign: seCss["text-align"] ?? "center",
3673
- fontFamily: seCss["font-family"] ?? "Roboto",
3674
- fontWeight: seCss["font-weight"] ?? "bold",
3675
- color: seCss["color"] ?? "#ffffff",
3676
- textShadow: seCss["text-shadow"] ?? "rgb(0, 0, 0) 1px 1px 1px",
3677
- lineHeight: seCss["line-height"] ?? 1,
3678
- background: "transparent",
3679
- ...seExtraTextCss(seCss, widget.text?.scrolling)
3680
- }
3681
- }, ctx);
4046
+ ctx
4047
+ );
3682
4048
  }
3683
4049
  function mapImage(widget, ctx) {
3684
- return buildUnit(widget, "image", {
3685
- content: {
3686
- src: widget.image?.src ?? "",
3687
- loop: true
3688
- }
3689
- }, ctx);
4050
+ return buildUnit(
4051
+ widget,
4052
+ "image",
4053
+ {
4054
+ content: {
4055
+ src: widget.image?.src ?? "",
4056
+ loop: true
4057
+ }
4058
+ },
4059
+ ctx
4060
+ );
3690
4061
  }
3691
4062
  function mapVideo(widget, ctx) {
3692
- return buildUnit(widget, "video", {
3693
- content: {
3694
- src: widget.video?.src ?? "",
3695
- volume: widget.video?.volume ?? 0.5,
3696
- loop: true,
3697
- muted: false
3698
- }
3699
- }, ctx);
4063
+ const unit = buildUnit(
4064
+ widget,
4065
+ "video",
4066
+ {
4067
+ content: {
4068
+ src: widget.video?.src ?? "",
4069
+ volume: widget.video?.volume ?? 0.5,
4070
+ loop: true,
4071
+ muted: false
4072
+ }
4073
+ },
4074
+ ctx
4075
+ );
4076
+ unit.layer.bounds.autoHeight = true;
4077
+ return unit;
3700
4078
  }
3701
4079
  var LUMIA_SNOW_WEBM = "https://storage.lumiastream.com/overlays/global/seasonal/snow.webm";
3702
4080
  function mapSnow(widget, ctx) {
3703
- return buildUnit(widget, "video", {
3704
- content: {
3705
- src: LUMIA_SNOW_WEBM,
3706
- volume: 0,
3707
- loop: true,
3708
- muted: true
3709
- }
3710
- }, ctx);
4081
+ return buildUnit(
4082
+ widget,
4083
+ "video",
4084
+ {
4085
+ content: {
4086
+ src: LUMIA_SNOW_WEBM,
4087
+ volume: 0,
4088
+ loop: true,
4089
+ muted: true
4090
+ }
4091
+ },
4092
+ ctx
4093
+ );
3711
4094
  }
3712
4095
  function mapReadout(widget, fallbackVar, ctx) {
3713
4096
  const explicitListener = widget.variables?.listener;
@@ -3717,7 +4100,9 @@ function mapReadout(widget, fallbackVar, ctx) {
3717
4100
  const variable = listenerToLumiaVariable(listener, ctx?.provider);
3718
4101
  const userTemplate = widget.text?.value ?? "";
3719
4102
  const inheritsText = userTemplate.trim().length > 0 && /\{[a-zA-Z]/.test(userTemplate);
3720
- const value = inheritsText ? translateSeText(userTemplate, listener, void 0, ctx?.provider, { context: "static" }) : `{{${variable}}}`;
4103
+ const value = inheritsText ? translateSeText(userTemplate, listener, void 0, ctx?.provider, {
4104
+ context: "static"
4105
+ }) : `{{${variable}}}`;
3721
4106
  const seCss = widget.text?.css ?? {};
3722
4107
  return buildUnit(
3723
4108
  widget,
@@ -3731,7 +4116,7 @@ function mapReadout(widget, fallbackVar, ctx) {
3731
4116
  fontWeight: seCss["font-weight"] ?? "bold",
3732
4117
  color: seCss["color"] ?? "#ffffff",
3733
4118
  textShadow: seCss["text-shadow"] ?? "rgb(0, 0, 0) 1px 1px 1px",
3734
- lineHeight: seCss["line-height"] ?? 1,
4119
+ lineHeight: seCss["line-height"] ?? "normal",
3735
4120
  background: "transparent",
3736
4121
  ...seExtraTextCss(seCss, widget.text?.scrolling)
3737
4122
  }
@@ -4196,9 +4581,11 @@ function mapGoal(widget, ctx) {
4196
4581
  source: charity ? charity.source : sourceFromListener(listener),
4197
4582
  image: "",
4198
4583
  audio: { decrement: "", increment: "", goal: "", goalFailed: "", volume: 1 },
4199
- // `null` (rather than `''`) is the cleaner contract — Goal/index.tsx:11's
4200
- // `isGoalExpired` treats any falsy as not-expired so both work at render time,
4201
- // but `null` survives JSON round-trips without ambiguity ('' vs unset).
4584
+ // `null` (rather than `''`) is the cleaner contract — `formatEndsIn`
4585
+ // in Goal/index.tsx treats both as "no deadline" so both work at
4586
+ // render time, but `null` survives JSON round-trips without
4587
+ // ambiguity ('' vs unset). The Goal module no longer hides itself
4588
+ // when the date passes; the "Ends in…" sublabel just disappears.
4202
4589
  endDate: v.endDate ?? null,
4203
4590
  goalAnimation: "",
4204
4591
  goalAnimationLoop: true,
@@ -4600,15 +4987,13 @@ function computeAllowedEventsFromSEListeners(seListeners, explicitListener) {
4600
4987
  });
4601
4988
  return allowedAlerts;
4602
4989
  }
4603
- function seScrollSpeedToMarqueeDurationSec(seSpeed) {
4604
- const n = typeof seSpeed === "number" && Number.isFinite(seSpeed) ? seSpeed : 6;
4605
- const clamped = Math.min(20, Math.max(1, n));
4606
- const duration = 60 / clamped;
4607
- return Math.min(120, Math.max(5, Math.round(duration * 10) / 10));
4990
+ var LUMIA_DEFAULT_MARQUEE_PIXELS_PER_SECOND = 20;
4991
+ function seScrollSpeedToPixelsPerSecond(_seSpeed) {
4992
+ return LUMIA_DEFAULT_MARQUEE_PIXELS_PER_SECOND;
4608
4993
  }
4609
4994
  function seTemplateToMarqueeTemplate(seTemplate) {
4610
4995
  if (typeof seTemplate !== "string" || !seTemplate.trim()) return void 0;
4611
- return seTemplate.split("{name}").join("{username}").split("{username}").join("{username}");
4996
+ return translateSeText(seTemplate);
4612
4997
  }
4613
4998
  function mapEventList(widget, ctx) {
4614
4999
  const v = widget.variables ?? {};
@@ -4621,7 +5006,7 @@ function mapEventList(widget, ctx) {
4621
5006
  const scrolling = seText?.scrolling;
4622
5007
  const isMarquee = !!(scrolling && scrolling.enabled === true);
4623
5008
  const itemTemplate = isMarquee ? seTemplateToMarqueeTemplate(seText?.value) : void 0;
4624
- const marqueeSpeed = isMarquee ? seScrollSpeedToMarqueeDurationSec(scrolling?.speed) : void 0;
5009
+ const marqueeSpeed = isMarquee ? seScrollSpeedToPixelsPerSecond(scrolling?.speed) : void 0;
4625
5010
  const marqueeDirection = isMarquee ? scrolling?.direction === "right" ? "right" : "left" : void 0;
4626
5011
  const theme = isMarquee ? "marquee" : v.theme ?? "simple";
4627
5012
  return buildUnit(
@@ -5056,6 +5441,47 @@ function mapTimer(widget, ctx) {
5056
5441
  function mapNowPlaying(widget, ctx) {
5057
5442
  const v = widget.variables ?? {};
5058
5443
  const source = v.provider === "youtube" ? "youtubemusic" : "spotify";
5444
+ const isMarquee = !!v.simpleDesign;
5445
+ const text = widget.text ?? {};
5446
+ const scrolling = text.scrolling ?? {};
5447
+ const seTextCss = text.css ?? {};
5448
+ const seTemplate = typeof text.value === "string" && text.value.trim().length > 0 ? text.value : "";
5449
+ const itemTemplate = seTemplate ? seTemplate.replace(/\{title\}/gi, "{{songTitle}}").replace(/\{artist\}/gi, "{{songArtist}}") : "{{songTitle}} - {{songArtist}}";
5450
+ const marqueeSpeed = 20;
5451
+ const marqueeDirection = scrolling.direction === "right" ? "right" : "left";
5452
+ const wantsShadow = text.enableShadow !== false;
5453
+ const importedCss = {
5454
+ borderRadius: "0px",
5455
+ borderStyle: "solid",
5456
+ borderWidth: "0px",
5457
+ borderColor: "transparent",
5458
+ fontSize: seTextCss["font-size"] ?? 24,
5459
+ textAlign: seTextCss["text-align"] ?? "left",
5460
+ fontFamily: seTextCss["font-family"] ?? "Be Vietnam Pro",
5461
+ fontWeight: seTextCss["font-weight"] ?? "bold",
5462
+ color: seTextCss["color"] ?? "#ffffff",
5463
+ textShadow: wantsShadow ? seTextCss["text-shadow"] ?? "rgb(0, 0, 0) 1px 1px 1px" : "none",
5464
+ // `normal` (not `1`) when SE didn't specify — see the same fix in
5465
+ // `build.ts::mapSeTextCssToModuleCss` and `basic.ts::mapText` for the
5466
+ // rationale. This path is the now-playing / current-song mapper, which
5467
+ // is the one the user noticed had a too-tight line-height in the
5468
+ // marquee render.
5469
+ lineHeight: seTextCss["line-height"] ?? "normal",
5470
+ background: "transparent"
5471
+ };
5472
+ if (typeof seTextCss["text-transform"] === "string")
5473
+ importedCss.textTransform = seTextCss["text-transform"];
5474
+ if (typeof seTextCss["font-style"] === "string")
5475
+ importedCss.fontStyle = seTextCss["font-style"];
5476
+ if (typeof seTextCss["text-decoration"] === "string")
5477
+ importedCss.textDecoration = seTextCss["text-decoration"];
5478
+ if (typeof seTextCss["letter-spacing"] === "string" || typeof seTextCss["letter-spacing"] === "number") {
5479
+ importedCss.letterSpacing = seTextCss["letter-spacing"];
5480
+ }
5481
+ if (seTextCss["-webkit-text-stroke-color"])
5482
+ importedCss["-webkit-text-stroke-color"] = seTextCss["-webkit-text-stroke-color"];
5483
+ if (seTextCss["-webkit-text-stroke-width"] != null)
5484
+ importedCss["-webkit-text-stroke-width"] = seTextCss["-webkit-text-stroke-width"];
5059
5485
  return buildUnit(
5060
5486
  widget,
5061
5487
  "nowplaying",
@@ -5064,7 +5490,7 @@ function mapNowPlaying(widget, ctx) {
5064
5490
  version: 1,
5065
5491
  type: "nowplaying",
5066
5492
  source,
5067
- theme: "basic",
5493
+ theme: isMarquee ? "marquee" : "basic",
5068
5494
  borderRadius: "30px",
5069
5495
  songTitle: `{{${source}_now_playing_song=Lumia Dream}}`,
5070
5496
  songArtist: `{{${source}_now_playing_artist}}`,
@@ -5072,20 +5498,14 @@ function mapNowPlaying(widget, ctx) {
5072
5498
  showImg: true,
5073
5499
  oneLineTitle: false,
5074
5500
  highlightColor: "inherit",
5501
+ ...isMarquee ? {
5502
+ marqueeSpeed,
5503
+ marqueeDirection,
5504
+ themeConfig: { itemTemplate }
5505
+ } : {},
5075
5506
  importMeta: buildImportMeta(widget, { simpleDesign: !!v.simpleDesign })
5076
5507
  },
5077
- css: {
5078
- borderRadius: "0px",
5079
- borderStyle: "solid",
5080
- borderWidth: "0px",
5081
- borderColor: "transparent",
5082
- lineHeight: 1,
5083
- textAlign: "left",
5084
- fontFamily: "Be Vietnam Pro",
5085
- color: "#ffffff",
5086
- textShadow: "rgb(0, 0, 0) 1px 1px 1px",
5087
- background: "transparent"
5088
- }
5508
+ css: importedCss
5089
5509
  },
5090
5510
  ctx
5091
5511
  );
@@ -5504,6 +5924,22 @@ function mapEmoteWall(widget, ctx) {
5504
5924
  );
5505
5925
  }
5506
5926
  function mapUnsupportedAsText(widget, ctx) {
5927
+ const userTemplate = typeof widget.text?.value === "string" ? widget.text.value.trim() : "";
5928
+ const inheritsText = userTemplate.length > 0 && /\{[a-zA-Z]/.test(userTemplate);
5929
+ if (inheritsText) {
5930
+ return buildUnit(
5931
+ widget,
5932
+ "text",
5933
+ {
5934
+ css: mapSeTextCssToModuleCss(widget, { fontSize: 24 }),
5935
+ content: {
5936
+ value: translateSeText(userTemplate),
5937
+ highlightColor: "inherit"
5938
+ }
5939
+ },
5940
+ ctx
5941
+ );
5942
+ }
5507
5943
  return buildUnit(
5508
5944
  widget,
5509
5945
  "text",
@@ -5847,7 +6283,13 @@ var READOUT_TYPES = {
5847
6283
  // twitch_session_chat_count, so we render it as a text layer with the variable inline.
5848
6284
  "se-widget-botcounter": "twitch_session_chat_count"
5849
6285
  };
5850
- var SEASONAL_IMAGE_TYPES = /* @__PURE__ */ new Set(["se-widget-halloween", "se-widget-xmas", "se-widget-valentine", "se-widget-easter", "se-widget-world-cup"]);
6286
+ var SEASONAL_IMAGE_TYPES = /* @__PURE__ */ new Set([
6287
+ "se-widget-halloween",
6288
+ "se-widget-xmas",
6289
+ "se-widget-valentine",
6290
+ "se-widget-easter",
6291
+ "se-widget-world-cup"
6292
+ ]);
5851
6293
  var GOAL_TYPES = /* @__PURE__ */ new Set([
5852
6294
  "se-widget-follower-goal",
5853
6295
  "se-widget-subscriber-goal",
@@ -5858,59 +6300,212 @@ var GOAL_TYPES = /* @__PURE__ */ new Set([
5858
6300
  "se-widget-tiltify-goal",
5859
6301
  "se-widget-merch-goal"
5860
6302
  ]);
5861
- 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"]);
6303
+ var EVENTLIST_TYPES = /* @__PURE__ */ new Set([
6304
+ "se-widget-event-list",
6305
+ "se-widget-follower-recent",
6306
+ "se-widget-subscriber-recent",
6307
+ "se-widget-cheer-recent",
6308
+ "se-widget-tip-recent"
6309
+ ]);
5862
6310
  function dispatch(widget, ctx) {
5863
6311
  const t = widget.type;
5864
- console.log("[SE-IMPORT] dispatch widget", {
5865
- seType: t,
5866
- name: widget.name,
5867
- listener: widget.listener,
5868
- listeners: widget.listeners,
5869
- variableKeys: widget.variables ? Object.keys(widget.variables) : [],
5870
- hasCss: !!widget.css
5871
- });
5872
6312
  if (isWidgetFlaggedOff(t)) {
5873
- return { unit: mapUnsupportedAsText(widget, ctx), status: "placeholder", lumiaType: "text", flaggedOff: true };
6313
+ return {
6314
+ unit: mapUnsupportedAsText(widget, ctx),
6315
+ status: "placeholder",
6316
+ lumiaType: "text",
6317
+ flaggedOff: true
6318
+ };
5874
6319
  }
5875
- if (t === "text") return { unit: mapText(widget, ctx), status: "direct", lumiaType: "text" };
5876
- if (t === "image") return { unit: mapImage(widget, ctx), status: "direct", lumiaType: "image" };
5877
- if (t === "video") return { unit: mapVideo(widget, ctx), status: "direct", lumiaType: "video" };
6320
+ if (t === "text")
6321
+ return { unit: mapText(widget, ctx), status: "direct", lumiaType: "text" };
6322
+ if (t === "image")
6323
+ return {
6324
+ unit: mapImage(widget, ctx),
6325
+ status: "direct",
6326
+ lumiaType: "image"
6327
+ };
6328
+ if (t === "video")
6329
+ return {
6330
+ unit: mapVideo(widget, ctx),
6331
+ status: "direct",
6332
+ lumiaType: "video"
6333
+ };
5878
6334
  if (t in READOUT_TYPES) {
5879
- return { unit: mapReadout(widget, READOUT_TYPES[t], ctx), status: "direct", lumiaType: "text" };
6335
+ return {
6336
+ unit: mapReadout(widget, READOUT_TYPES[t], ctx),
6337
+ status: "direct",
6338
+ lumiaType: "text"
6339
+ };
5880
6340
  }
5881
- if (GOAL_TYPES.has(t)) return { unit: mapGoal(widget, ctx), status: "direct", lumiaType: "goal" };
6341
+ if (GOAL_TYPES.has(t))
6342
+ return { unit: mapGoal(widget, ctx), status: "direct", lumiaType: "goal" };
5882
6343
  if ((t === "se-widget-alert-box" || t === "se-widget-alert-box-merch") && hasSECustomCode(widget)) {
5883
- return { unit: mapUnsupportedAsText(widget, ctx), status: "placeholder", lumiaType: "text", flaggedOff: true };
5884
- }
5885
- if (t === "se-widget-alert-box") return { unit: mapAlertBox(widget, {}, ctx), status: "direct", lumiaType: "alert" };
5886
- if (t === "se-widget-alert-box-merch") return { unit: mapAlertBox(widget, { onlyEvents: ["merch"] }, ctx), status: "direct", lumiaType: "alert" };
5887
- if (t === "se-widget-donor-drive-alert") return { unit: mapSingleAlert(widget, "donordrive-donation", ctx), status: "direct", lumiaType: "alert" };
5888
- if (t === "se-widget-extralife-alert") return { unit: mapSingleAlert(widget, "extralife-donation", ctx), status: "direct", lumiaType: "alert" };
5889
- if (t === "se-widget-tiltify-alert") return { unit: mapSingleAlert(widget, "tiltify-campaignDonation", ctx), status: "direct", lumiaType: "alert" };
5890
- if (EVENTLIST_TYPES.has(t)) return { unit: mapEventList(widget, ctx), status: "direct", lumiaType: "eventlist" };
5891
- if (t === "se-widget-top-tippers-list") return { unit: mapViewerProfilesLeaderboard(widget, "tips", ctx), status: "direct", lumiaType: "viewerprofiles" };
5892
- if (t === "se-widget-top-cheerers-list") return { unit: mapViewerProfilesLeaderboard(widget, "cheers", ctx), status: "direct", lumiaType: "viewerprofiles" };
5893
- if (t === "se-widget-top-gifters-list") return { unit: mapViewerProfilesLeaderboard(widget, "gifts", ctx), status: "direct", lumiaType: "viewerprofiles" };
5894
- if (t === "se-widget-credit-roll") return { unit: mapCredits(widget, ctx), status: "partial", lumiaType: "credits" };
5895
- if (t === "se-widget-twitch-chat") return { unit: mapChatbox(widget, ctx), status: "partial", lumiaType: "chatbox" };
5896
- 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" };
5897
- if (t === "se-widget-countdown") return { unit: mapTimer(widget, ctx), status: "partial", lumiaType: "timer" };
5898
- if (t === "se-widget-current-song") return { unit: mapNowPlaying(widget, ctx), status: "partial", lumiaType: "nowplaying" };
5899
- if (t === "se-widget-kappagen") return { unit: mapKappagen(widget, ctx), status: "partial", lumiaType: "emotealert" };
5900
- if (t === "se-widget-emote-wall") return { unit: mapEmoteWall(widget, ctx), status: "partial", lumiaType: "emotebox" };
5901
- if (t === "se-widget-giveaway") return { unit: mapGiveaway(widget, ctx), status: "direct", lumiaType: "raffle" };
5902
- if (t === "se-widget-media-share") return { unit: mapMediaShare(widget, ctx), status: "partial", lumiaType: "songrequest" };
5903
- if (t === "se-widget-hype-cup") return { unit: mapHypeCup(widget, ctx), status: "partial", lumiaType: "tipjar" };
5904
- if (t === "se-widget-bit-boss") return { unit: mapStreamBoss(widget, ctx), status: "partial", lumiaType: "streamboss" };
5905
- if (t === "se-widget-hype-train") return { unit: mapHypetrain(widget, ctx), status: "partial", lumiaType: "hypetrain" };
6344
+ return {
6345
+ unit: mapUnsupportedAsText(widget, ctx),
6346
+ status: "placeholder",
6347
+ lumiaType: "text",
6348
+ flaggedOff: true
6349
+ };
6350
+ }
6351
+ if (t === "se-widget-alert-box")
6352
+ return {
6353
+ unit: mapAlertBox(widget, {}, ctx),
6354
+ status: "direct",
6355
+ lumiaType: "alert"
6356
+ };
6357
+ if (t === "se-widget-alert-box-merch")
6358
+ return {
6359
+ unit: mapAlertBox(widget, { onlyEvents: ["merch"] }, ctx),
6360
+ status: "direct",
6361
+ lumiaType: "alert"
6362
+ };
6363
+ if (t === "se-widget-donor-drive-alert")
6364
+ return {
6365
+ unit: mapSingleAlert(widget, "donordrive-donation", ctx),
6366
+ status: "direct",
6367
+ lumiaType: "alert"
6368
+ };
6369
+ if (t === "se-widget-extralife-alert")
6370
+ return {
6371
+ unit: mapSingleAlert(widget, "extralife-donation", ctx),
6372
+ status: "direct",
6373
+ lumiaType: "alert"
6374
+ };
6375
+ if (t === "se-widget-tiltify-alert")
6376
+ return {
6377
+ unit: mapSingleAlert(widget, "tiltify-campaignDonation", ctx),
6378
+ status: "direct",
6379
+ lumiaType: "alert"
6380
+ };
6381
+ if (EVENTLIST_TYPES.has(t))
6382
+ return {
6383
+ unit: mapEventList(widget, ctx),
6384
+ status: "direct",
6385
+ lumiaType: "eventlist"
6386
+ };
6387
+ if (t === "se-widget-top-tippers-list")
6388
+ return {
6389
+ unit: mapViewerProfilesLeaderboard(widget, "tips", ctx),
6390
+ status: "direct",
6391
+ lumiaType: "viewerprofiles"
6392
+ };
6393
+ if (t === "se-widget-top-cheerers-list")
6394
+ return {
6395
+ unit: mapViewerProfilesLeaderboard(widget, "cheers", ctx),
6396
+ status: "direct",
6397
+ lumiaType: "viewerprofiles"
6398
+ };
6399
+ if (t === "se-widget-top-gifters-list")
6400
+ return {
6401
+ unit: mapViewerProfilesLeaderboard(widget, "gifts", ctx),
6402
+ status: "direct",
6403
+ lumiaType: "viewerprofiles"
6404
+ };
6405
+ if (t === "se-widget-credit-roll")
6406
+ return {
6407
+ unit: mapCredits(widget, ctx),
6408
+ status: "partial",
6409
+ lumiaType: "credits"
6410
+ };
6411
+ if (t === "se-widget-twitch-chat")
6412
+ return {
6413
+ unit: mapChatbox(widget, ctx),
6414
+ status: "partial",
6415
+ lumiaType: "chatbox"
6416
+ };
6417
+ if (t === "se-widget-image-rotator" || t === "se-widget-merch-products-rotator")
6418
+ return {
6419
+ unit: mapSlideshow(widget, ctx),
6420
+ status: t === "se-widget-merch-products-rotator" ? "partial" : "direct",
6421
+ lumiaType: "slideshow"
6422
+ };
6423
+ if (t === "se-widget-countdown")
6424
+ return {
6425
+ unit: mapTimer(widget, ctx),
6426
+ status: "partial",
6427
+ lumiaType: "timer"
6428
+ };
6429
+ if (t === "se-widget-current-song")
6430
+ return {
6431
+ unit: mapNowPlaying(widget, ctx),
6432
+ status: "partial",
6433
+ lumiaType: "nowplaying"
6434
+ };
6435
+ if (t === "se-widget-kappagen")
6436
+ return {
6437
+ unit: mapKappagen(widget, ctx),
6438
+ status: "partial",
6439
+ lumiaType: "emotealert"
6440
+ };
6441
+ if (t === "se-widget-emote-wall")
6442
+ return {
6443
+ unit: mapEmoteWall(widget, ctx),
6444
+ status: "partial",
6445
+ lumiaType: "emotebox"
6446
+ };
6447
+ if (t === "se-widget-giveaway")
6448
+ return {
6449
+ unit: mapGiveaway(widget, ctx),
6450
+ status: "direct",
6451
+ lumiaType: "raffle"
6452
+ };
6453
+ if (t === "se-widget-media-share")
6454
+ return {
6455
+ unit: mapMediaShare(widget, ctx),
6456
+ status: "partial",
6457
+ lumiaType: "songrequest"
6458
+ };
6459
+ if (t === "se-widget-hype-cup")
6460
+ return {
6461
+ unit: mapHypeCup(widget, ctx),
6462
+ status: "partial",
6463
+ lumiaType: "tipjar"
6464
+ };
6465
+ if (t === "se-widget-bit-boss")
6466
+ return {
6467
+ unit: mapStreamBoss(widget, ctx),
6468
+ status: "partial",
6469
+ lumiaType: "streamboss"
6470
+ };
6471
+ if (t === "se-widget-hype-train")
6472
+ return {
6473
+ unit: mapHypetrain(widget, ctx),
6474
+ status: "partial",
6475
+ lumiaType: "hypetrain"
6476
+ };
5906
6477
  if (t === "se-widget-custom-event-list" && isHypetrainCustomWidget(widget)) {
5907
- return { unit: mapHypetrain(widget, ctx), status: "partial", lumiaType: "hypetrain" };
6478
+ return {
6479
+ unit: mapHypetrain(widget, ctx),
6480
+ status: "partial",
6481
+ lumiaType: "hypetrain"
6482
+ };
5908
6483
  }
5909
- if (t === "se-widget-custom-event-list") return { unit: mapCustom(widget, ctx), status: "partial", lumiaType: "custom" };
5910
- if (t === "se-widget-snow") return { unit: mapSnow(widget, ctx), status: "direct", lumiaType: "video" };
5911
- if (t === "se-widget-halloween-2019") return { unit: mapSlideshow(widget, ctx), status: "direct", lumiaType: "slideshow" };
5912
- if (SEASONAL_IMAGE_TYPES.has(t)) return { unit: mapImage(widget, ctx), status: "direct", lumiaType: "image" };
5913
- return { unit: mapUnsupportedAsText(widget, ctx), status: "placeholder", lumiaType: "text" };
6484
+ if (t === "se-widget-custom-event-list")
6485
+ return {
6486
+ unit: mapCustom(widget, ctx),
6487
+ status: "partial",
6488
+ lumiaType: "custom"
6489
+ };
6490
+ if (t === "se-widget-snow")
6491
+ return { unit: mapSnow(widget, ctx), status: "direct", lumiaType: "video" };
6492
+ if (t === "se-widget-halloween-2019")
6493
+ return {
6494
+ unit: mapSlideshow(widget, ctx),
6495
+ status: "direct",
6496
+ lumiaType: "slideshow"
6497
+ };
6498
+ if (SEASONAL_IMAGE_TYPES.has(t))
6499
+ return {
6500
+ unit: mapImage(widget, ctx),
6501
+ status: "direct",
6502
+ lumiaType: "image"
6503
+ };
6504
+ return {
6505
+ unit: mapUnsupportedAsText(widget, ctx),
6506
+ status: "placeholder",
6507
+ lumiaType: "text"
6508
+ };
5914
6509
  }
5915
6510
  function recordCoverage(coverage, result, seType) {
5916
6511
  const existing = coverage.mappings.find((m) => m.seType === seType);
@@ -5918,7 +6513,12 @@ function recordCoverage(coverage, result, seType) {
5918
6513
  existing.count += 1;
5919
6514
  return;
5920
6515
  }
5921
- coverage.mappings.push({ seType, lumiaType: result.lumiaType, status: result.status, count: 1 });
6516
+ coverage.mappings.push({
6517
+ seType,
6518
+ lumiaType: result.lumiaType,
6519
+ status: result.status,
6520
+ count: 1
6521
+ });
5922
6522
  }
5923
6523
 
5924
6524
  // src/se-import/assets.ts
@@ -9003,17 +9603,6 @@ function importSEBootstrap(bootstrap) {
9003
9603
  notes: []
9004
9604
  };
9005
9605
  const provider = readBootstrapProvider(bootstrap);
9006
- console.log("[SE-IMPORT] bootstrap received", {
9007
- overlayId: seOverlay?._id,
9008
- name: seOverlay?.name,
9009
- canvas: {
9010
- width: seOverlay?.settings?.width,
9011
- height: seOverlay?.settings?.height
9012
- },
9013
- widgetCount: widgets.length,
9014
- widgetTypes: widgets.map((w) => w.type),
9015
- provider
9016
- });
9017
9606
  const importCanvas = {
9018
9607
  width: seOverlay?.settings?.width ?? 1920,
9019
9608
  height: seOverlay?.settings?.height ?? 1080
@@ -9165,12 +9754,6 @@ function importSEBootstrap(bootstrap) {
9165
9754
  layers_count: layers.length,
9166
9755
  hasFullSettings: true
9167
9756
  };
9168
- console.log("[SE-IMPORT] import complete", {
9169
- overlayName: overlay.name,
9170
- layerCount: overlay.layers_count,
9171
- coverage,
9172
- reviewItemCount: reviewItems.length
9173
- });
9174
9757
  return { overlay, coverage, reviewItems };
9175
9758
  }
9176
9759
  function applyReviewAction(result, moduleId, action, payload) {