@lumiastream/ui 0.2.8-alpha.20 → 0.2.8-alpha.23

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
@@ -3131,45 +3131,104 @@ var SE_TO_LUMIA_PLACEHOLDER = {
3131
3131
  total: "total",
3132
3132
  gifts: "giftAmount"
3133
3133
  };
3134
- function listenerPlaceholderContext(listener) {
3135
- if (listener === "follower-latest") return { name: "twitch_last_follower", username: "twitch_last_follower" };
3136
- if (listener === "subscriber-latest" || listener === "subscriber-new-latest" || listener === "subscriber-resub-latest") {
3137
- return { name: "twitch_last_subscriber", username: "twitch_last_subscriber" };
3138
- }
3139
- if (listener === "subscriber-gifted-latest") return { name: "twitch_last_gifter", username: "twitch_last_gifter", amount: "twitch_last_gifter_amount", count: "twitch_last_gifter_amount" };
3140
- if (listener === "subscriber-alltime-gifter") return { name: "twitch_alltime_top_gifter", username: "twitch_alltime_top_gifter", amount: "twitch_alltime_top_gifter_amount", count: "twitch_alltime_top_gifter_amount" };
3141
- if (listener === "cheer-latest") return { name: "twitch_last_bit", username: "twitch_last_bit", amount: "twitch_last_bit_amount", count: "twitch_last_bit_amount" };
3142
- if (listener === "tip-latest" || listener === "latest_donator") {
3143
- return { name: "latest_donator", username: "latest_donator", amount: "latest_donator_amount", count: "latest_donator_amount", currency: "latest_donator_currency_symbol" };
3144
- }
3145
- if (listener === "raid-latest") return { name: "twitch_last_raider", username: "twitch_last_raider", amount: "twitch_last_raid_amount", count: "twitch_last_raid_amount" };
3146
- if (listener === "follower-session") return { amount: "twitch_session_follower_count", count: "twitch_session_follower_count" };
3147
- if (listener === "subscriber-session") return { amount: "twitch_session_subscribers_count", count: "twitch_session_subscribers_count" };
3148
- if (listener === "cheer-session") return { amount: "twitch_session_bits_count", count: "twitch_session_bits_count" };
3149
- if (listener === "tip-session") return { amount: "session_donation_amount", count: "session_donation_count", currency: "session_donation_amount_currency_symbol" };
3150
- if (listener === "follower-total") return { amount: "twitch_total_follower_count", count: "twitch_total_follower_count" };
3151
- if (listener === "subscriber-total") return { amount: "twitch_total_subscriber_count", count: "twitch_total_subscriber_count" };
3152
- if (listener === "cheer-total") return { amount: "twitch_total_bits_count", count: "twitch_total_bits_count" };
3153
- if (listener === "tip-total") return { amount: "total_donation_amount", count: "total_donation_amount", currency: "total_donation_amount_currency_symbol" };
3154
- if (listener === "tip-count") return { amount: "donation_count", count: "donation_count" };
3155
- if (listener === "cheer-session-top-donation") return { amount: "twitch_session_top_cheer_amount", count: "twitch_session_top_cheer_amount" };
3156
- if (listener === "cheer-session-top-donator") return { name: "twitch_session_top_cheerer", username: "twitch_session_top_cheerer", amount: "twitch_session_top_cheerer_amount", count: "twitch_session_top_cheerer_amount" };
3157
- if (listener === "tip-session-top-donation") return { amount: "session_top_donator_amount", count: "session_top_donator_amount", currency: "session_top_donator_currency_symbol" };
3158
- if (listener === "tip-session-top-donator") return { name: "session_top_donator", username: "session_top_donator", amount: "session_top_donator_amount", count: "session_top_donator_amount", currency: "session_top_donator_currency_symbol" };
3159
- if (listener === "twitch_session_chat_count") return { wins: "twitch_session_chat_count", count: "twitch_session_chat_count", amount: "twitch_session_chat_count" };
3160
- if (listener === "merch-latest" || listener === "purchase-latest") {
3161
- return {
3162
- name: "fourthwall_last_order.username",
3163
- username: "fourthwall_last_order.username",
3164
- amount: "fourthwall_last_order.amount",
3165
- count: "fourthwall_last_order.itemsCount",
3166
- currency: "fourthwall_last_order.currency",
3167
- items: "fourthwall_last_order.items",
3168
- item: "fourthwall_last_order.firstItem.name",
3169
- message: "fourthwall_last_order.message"
3170
- };
3134
+ var TWITCH_CONTEXTS = {
3135
+ "follower-latest": { name: "twitch_last_follower", username: "twitch_last_follower" },
3136
+ "subscriber-latest": { name: "twitch_last_subscriber", username: "twitch_last_subscriber" },
3137
+ "subscriber-new-latest": { name: "twitch_last_subscriber", username: "twitch_last_subscriber" },
3138
+ "subscriber-resub-latest": { name: "twitch_last_subscriber", username: "twitch_last_subscriber" },
3139
+ "subscriber-gifted-latest": { name: "twitch_last_gifter", username: "twitch_last_gifter", amount: "twitch_last_gifter_amount", count: "twitch_last_gifter_amount" },
3140
+ "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" },
3141
+ "cheer-latest": { name: "twitch_last_bit", username: "twitch_last_bit", amount: "twitch_last_bit_amount", count: "twitch_last_bit_amount" },
3142
+ "tip-latest": { name: "latest_donator", username: "latest_donator", amount: "latest_donator_amount", count: "latest_donator_amount", currency: "latest_donator_currency_symbol" },
3143
+ latest_donator: { name: "latest_donator", username: "latest_donator", amount: "latest_donator_amount", count: "latest_donator_amount", currency: "latest_donator_currency_symbol" },
3144
+ "raid-latest": { name: "twitch_last_raider", username: "twitch_last_raider", amount: "twitch_last_raid_amount", count: "twitch_last_raid_amount" },
3145
+ // Session counters: count/amount = the running session count.
3146
+ "follower-session": { amount: "twitch_session_follower_count", count: "twitch_session_follower_count" },
3147
+ "subscriber-session": { amount: "twitch_session_subscribers_count", count: "twitch_session_subscribers_count" },
3148
+ "cheer-session": { amount: "twitch_session_bits_count", count: "twitch_session_bits_count" },
3149
+ "tip-session": { amount: "session_donation_amount", count: "session_donation_count", currency: "session_donation_amount_currency_symbol" },
3150
+ // Totals (all-time): count/amount = total counter.
3151
+ "follower-total": { amount: "twitch_total_follower_count", count: "twitch_total_follower_count" },
3152
+ "subscriber-total": { amount: "twitch_total_subscriber_count", count: "twitch_total_subscriber_count" },
3153
+ "cheer-total": { amount: "twitch_total_bits_count", count: "twitch_total_bits_count" },
3154
+ "tip-total": { amount: "total_donation_amount", count: "total_donation_amount", currency: "total_donation_amount_currency_symbol" },
3155
+ "tip-count": { amount: "donation_count", count: "donation_count" },
3156
+ // Top cheer / cheerer / donator session readouts.
3157
+ "cheer-session-top-donation": { amount: "twitch_session_top_cheer_amount", count: "twitch_session_top_cheer_amount" },
3158
+ "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" },
3159
+ "tip-session-top-donation": { amount: "session_top_donator_amount", count: "session_top_donator_amount", currency: "session_top_donator_currency_symbol" },
3160
+ "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" },
3161
+ // Botcounter — SE uses `{wins}` for the running chat count; the dispatcher's
3162
+ // fallbackVar for `se-widget-botcounter` is `twitch_session_chat_count`, so
3163
+ // when the listener arg lands as that var name we treat it as the wins source.
3164
+ twitch_session_chat_count: { wins: "twitch_session_chat_count", count: "twitch_session_chat_count", amount: "twitch_session_chat_count" },
3165
+ // Merch readouts — SE used to ship via their own shop, now most users route
3166
+ // through Fourthwall. Lumia exposes `fourthwall_last_order` as a full object:
3167
+ // { username, email, friendlyId, amount, currency, message, items,
3168
+ // itemsCount, itemsDetailed, orderItems, itemImages, firstItem }
3169
+ // (see fourthwall.manager.ts ORDER_PLACED handler). The template engine
3170
+ // supports dotted access (`dotNotationDig`) so we pluck the right field.
3171
+ "merch-latest": {
3172
+ name: "fourthwall_last_order.username",
3173
+ username: "fourthwall_last_order.username",
3174
+ amount: "fourthwall_last_order.amount",
3175
+ count: "fourthwall_last_order.itemsCount",
3176
+ currency: "fourthwall_last_order.currency",
3177
+ items: "fourthwall_last_order.items",
3178
+ item: "fourthwall_last_order.firstItem.name",
3179
+ message: "fourthwall_last_order.message"
3180
+ },
3181
+ "purchase-latest": {
3182
+ name: "fourthwall_last_order.username",
3183
+ username: "fourthwall_last_order.username",
3184
+ amount: "fourthwall_last_order.amount",
3185
+ count: "fourthwall_last_order.itemsCount",
3186
+ currency: "fourthwall_last_order.currency",
3187
+ items: "fourthwall_last_order.items",
3188
+ item: "fourthwall_last_order.firstItem.name",
3189
+ message: "fourthwall_last_order.message"
3171
3190
  }
3172
- return {};
3191
+ };
3192
+ var KICK_CONTEXTS = {
3193
+ "follower-latest": { name: "kick_last_follower", username: "kick_last_follower" },
3194
+ "subscriber-latest": { name: "kick_last_subscriber", username: "kick_last_subscriber" },
3195
+ "subscriber-new-latest": { name: "kick_last_subscriber", username: "kick_last_subscriber" },
3196
+ "subscriber-resub-latest": { name: "kick_last_subscriber", username: "kick_last_subscriber" },
3197
+ "subscriber-gifted-latest": { name: "kick_last_gifter", username: "kick_last_gifter", amount: "kick_last_gifter_amount", count: "kick_last_gifter_amount" },
3198
+ "cheer-latest": { name: "kick_last_kicks", username: "kick_last_kicks", amount: "kick_last_kicks_amount", count: "kick_last_kicks_amount" },
3199
+ "raid-latest": { name: "kick_last_host", username: "kick_last_host", amount: "kick_last_host_amount", count: "kick_last_host_amount" },
3200
+ "follower-session": { amount: "kick_session_follower_count", count: "kick_session_follower_count" },
3201
+ "subscriber-session": { amount: "kick_session_subscriber_count", count: "kick_session_subscriber_count" },
3202
+ "cheer-session": { amount: "kick_session_kicks_count", count: "kick_session_kicks_count" },
3203
+ "subscriber-gifted-session": { amount: "kick_session_gifts_count", count: "kick_session_gifts_count" },
3204
+ "follower-total": { amount: "kick_total_follower_count", count: "kick_total_follower_count" },
3205
+ "subscriber-total": { amount: "kick_total_subscriber_count", count: "kick_total_subscriber_count" },
3206
+ "cheer-total": { amount: "kick_total_kicks_count", count: "kick_total_kicks_count" },
3207
+ "follower-week": { amount: "kick_week_follower_count", count: "kick_week_follower_count" },
3208
+ "follower-month": { amount: "kick_month_follower_count", count: "kick_month_follower_count" },
3209
+ "subscriber-week": { amount: "kick_week_subscriber_count", count: "kick_week_subscriber_count" },
3210
+ "subscriber-month": { amount: "kick_month_subscriber_count", count: "kick_month_subscriber_count" }
3211
+ };
3212
+ var YOUTUBE_CONTEXTS = {
3213
+ "follower-latest": { name: "youtube_last_subscriber", username: "youtube_last_subscriber" },
3214
+ "subscriber-latest": { name: "youtube_last_member", username: "youtube_last_member" },
3215
+ "subscriber-new-latest": { name: "youtube_last_member", username: "youtube_last_member" },
3216
+ "subscriber-resub-latest": { name: "youtube_last_member", username: "youtube_last_member" },
3217
+ "subscriber-gifted-latest": { name: "youtube_last_gift_member", username: "youtube_last_gift_member" },
3218
+ "cheer-latest": { name: "youtube_last_superchatter", username: "youtube_last_superchatter" },
3219
+ "follower-session": { amount: "youtube_session_subscriber_count", count: "youtube_session_subscriber_count" },
3220
+ "subscriber-session": { amount: "youtube_session_member_count", count: "youtube_session_member_count" },
3221
+ "cheer-session": { amount: "youtube_session_superchat_count", count: "youtube_session_superchat_count" },
3222
+ "follower-total": { amount: "youtube_total_subscriber_count", count: "youtube_total_subscriber_count" },
3223
+ "subscriber-total": { amount: "youtube_total_member_count", count: "youtube_total_member_count" },
3224
+ "follower-week": { amount: "youtube_week_subscriber_count", count: "youtube_week_subscriber_count" },
3225
+ "follower-month": { amount: "youtube_month_subscriber_count", count: "youtube_month_subscriber_count" },
3226
+ "subscriber-week": { amount: "youtube_week_member_count", count: "youtube_week_member_count" },
3227
+ "subscriber-month": { amount: "youtube_month_member_count", count: "youtube_month_member_count" }
3228
+ };
3229
+ function listenerPlaceholderContext(listener, provider = "twitch") {
3230
+ const platformTable = provider === "kick" ? KICK_CONTEXTS : provider === "youtube" ? YOUTUBE_CONTEXTS : null;
3231
+ return platformTable?.[listener] ?? TWITCH_CONTEXTS[listener] ?? {};
3173
3232
  }
3174
3233
  var SE_WIDGET_TYPE_LABELS = {
3175
3234
  "subscriber-latest": "Latest subscriber",
@@ -3205,17 +3264,57 @@ function defaultLabelForSEWidget(widget) {
3205
3264
  const tail = words.slice(1).join(" ");
3206
3265
  return tail ? `${head} ${tail}` : head;
3207
3266
  }
3208
- function translateSeText(input, listener, passthrough) {
3267
+ function translateSeText(input, listener, passthrough, provider) {
3209
3268
  if (!input) return "";
3210
- const ctx = listener ? listenerPlaceholderContext(listener) : {};
3269
+ const ctx = listener ? listenerPlaceholderContext(listener, provider) : {};
3211
3270
  return input.replace(/\{([a-zA-Z][a-zA-Z0-9_.]*)\}/g, (_match, raw) => {
3212
3271
  if (passthrough?.has(raw)) return `{{${raw}}}`;
3213
3272
  const mapped = ctx[raw] ?? SE_TO_LUMIA_PLACEHOLDER[raw] ?? raw;
3214
3273
  return `{{${mapped}}}`;
3215
3274
  });
3216
3275
  }
3217
- function listenerToLumiaVariable(listener) {
3276
+ var KICK_LISTENER_VARS = {
3277
+ "follower-latest": "kick_last_follower",
3278
+ "subscriber-latest": "kick_last_subscriber",
3279
+ "subscriber-new-latest": "kick_last_subscriber",
3280
+ "subscriber-resub-latest": "kick_last_subscriber",
3281
+ "subscriber-gifted-latest": "kick_last_gifter",
3282
+ "cheer-latest": "kick_last_kicks",
3283
+ "raid-latest": "kick_last_host",
3284
+ "follower-session": "kick_session_follower_count",
3285
+ "follower-week": "kick_week_follower_count",
3286
+ "follower-month": "kick_month_follower_count",
3287
+ "follower-total": "kick_total_follower_count",
3288
+ "subscriber-session": "kick_session_subscriber_count",
3289
+ "subscriber-week": "kick_week_subscriber_count",
3290
+ "subscriber-month": "kick_month_subscriber_count",
3291
+ "subscriber-total": "kick_total_subscriber_count",
3292
+ "subscriber-gifted-session": "kick_session_gifts_count",
3293
+ "cheer-session": "kick_session_kicks_count",
3294
+ "cheer-total": "kick_total_kicks_count"
3295
+ };
3296
+ var YOUTUBE_LISTENER_VARS = {
3297
+ "follower-latest": "youtube_last_subscriber",
3298
+ "subscriber-latest": "youtube_last_member",
3299
+ "subscriber-new-latest": "youtube_last_member",
3300
+ "subscriber-resub-latest": "youtube_last_member",
3301
+ "subscriber-gifted-latest": "youtube_last_gift_member",
3302
+ "cheer-latest": "youtube_last_superchatter",
3303
+ "follower-session": "youtube_session_subscriber_count",
3304
+ "follower-week": "youtube_week_subscriber_count",
3305
+ "follower-month": "youtube_month_subscriber_count",
3306
+ "follower-total": "youtube_total_subscriber_count",
3307
+ "subscriber-session": "youtube_session_member_count",
3308
+ "subscriber-week": "youtube_week_member_count",
3309
+ "subscriber-month": "youtube_month_member_count",
3310
+ "subscriber-total": "youtube_total_member_count",
3311
+ "subscriber-gifted-session": "youtube_session_gift_members_count",
3312
+ "cheer-session": "youtube_session_superchat_count"
3313
+ };
3314
+ function listenerToLumiaVariable(listener, provider = "twitch") {
3218
3315
  if (typeof listener !== "string" || !listener) return "username";
3316
+ if (provider === "kick" && KICK_LISTENER_VARS[listener]) return KICK_LISTENER_VARS[listener];
3317
+ if (provider === "youtube" && YOUTUBE_LISTENER_VARS[listener]) return YOUTUBE_LISTENER_VARS[listener];
3219
3318
  const m = {
3220
3319
  // Latest events
3221
3320
  "follower-latest": "twitch_last_follower",
@@ -3513,10 +3612,10 @@ function mapReadout(widget, fallbackVar, ctx) {
3513
3612
  const explicit = typeof explicitListener === "string" ? explicitListener : null;
3514
3613
  const widgetListener = typeof widget.listener === "string" ? widget.listener : null;
3515
3614
  const listener = explicit ?? widgetListener ?? fallbackVar;
3516
- const variable = listenerToLumiaVariable(listener);
3615
+ const variable = listenerToLumiaVariable(listener, ctx?.provider);
3517
3616
  const userTemplate = widget.text?.value ?? "";
3518
3617
  const inheritsText = userTemplate.trim().length > 0 && /\{[a-zA-Z]/.test(userTemplate);
3519
- const value = inheritsText ? translateSeText(userTemplate, listener) : `{{${variable}}}`;
3618
+ const value = inheritsText ? translateSeText(userTemplate, listener, void 0, ctx?.provider) : `{{${variable}}}`;
3520
3619
  const seCss = widget.text?.css ?? {};
3521
3620
  return buildUnit(widget, "text", {
3522
3621
  content: { value, highlightColor: "inherit" },
@@ -3534,34 +3633,99 @@ function mapReadout(widget, fallbackVar, ctx) {
3534
3633
  }, ctx);
3535
3634
  }
3536
3635
 
3537
- // src/se-import/mappers/alert.ts
3538
- var SE_EVENT_TO_LUMIA_ALERT = {
3539
- follower: "twitch-follower",
3540
- subscriber: "twitch-subscriber",
3541
- tip: "streamelements-donation",
3542
- cheer: "twitch-bits",
3543
- raid: "twitch-raid",
3544
- // Merch / shop-order — Lumia's Fourthwall integration is the data path most
3545
- // users now have wired up since SE deprecated their own merch flow in favor
3546
- // of Fourthwall. `fourthwall-shopOrder` is the alert key from
3547
- // LumiaAlertValues / activity.types.ts.
3548
- merch: "fourthwall-shopOrder",
3549
- purchase: "fourthwall-shopOrder",
3550
- charityCampaignDonation: "twitch-charityDonation"
3551
- // NOTE — two SE events intentionally NOT mapped:
3552
- // • `host` → previously `twitch-raid`. Twitch deprecated hosting in
3553
- // late 2022 so the event never fires on the platform anymore. Even
3554
- // if it did, host payloads don't carry a viewer count (raids do),
3555
- // so the rendered alert would read "{name} raided with ?? viewers".
3556
- // Drop the mapping; SE widgets configured with a `host` block fall
3557
- // through to the unsupported placeholder rather than producing
3558
- // misleading raid alerts.
3559
- // • `redemption` → SE's `redemption` event is the legacy SE Store
3560
- // redemption (their own loyalty-point store purchases), NOT Twitch
3561
- // channel-point redemptions. Different payload, different semantics.
3562
- // SE Store is mostly deprecated and seldom used; dropping the
3563
- // mapping prevents misleading "channel-point redemption" alerts.
3636
+ // src/se-import/mappers/provider-alerts.ts
3637
+ var ALERT_BOX_KEYS = {
3638
+ twitch: {
3639
+ follower: "twitch-follower",
3640
+ subscriber: "twitch-subscriber",
3641
+ tip: "streamelements-donation",
3642
+ cheer: "twitch-bits",
3643
+ raid: "twitch-raid",
3644
+ merch: "fourthwall-shopOrder",
3645
+ purchase: "fourthwall-shopOrder",
3646
+ charityCampaignDonation: "twitch-charityDonation"
3647
+ },
3648
+ kick: {
3649
+ follower: "kick-follower",
3650
+ subscriber: "kick-subscriber",
3651
+ tip: "streamelements-donation",
3652
+ cheer: "kick-kicks",
3653
+ raid: "kick-host",
3654
+ host: "kick-host",
3655
+ merch: "fourthwall-shopOrder",
3656
+ purchase: "fourthwall-shopOrder"
3657
+ },
3658
+ youtube: {
3659
+ follower: "youtube-subscriber",
3660
+ subscriber: "youtube-member",
3661
+ tip: "streamelements-donation",
3662
+ cheer: "youtube-superchat",
3663
+ merch: "fourthwall-shopOrder",
3664
+ purchase: "fourthwall-shopOrder"
3665
+ }
3666
+ };
3667
+ var KAPPAGEN_KEYS = {
3668
+ twitch: {
3669
+ follower: "twitch-follower",
3670
+ subscriber: "twitch-subscriber",
3671
+ cheer: "twitch-bits",
3672
+ raid: "twitch-raid",
3673
+ tip: "streamelements-donation"
3674
+ },
3675
+ kick: {
3676
+ follower: "kick-follower",
3677
+ subscriber: "kick-subscriber",
3678
+ cheer: "kick-kicks",
3679
+ raid: "kick-host",
3680
+ tip: "streamelements-donation"
3681
+ },
3682
+ youtube: {
3683
+ follower: "youtube-subscriber",
3684
+ subscriber: "youtube-member",
3685
+ cheer: "youtube-superchat",
3686
+ tip: "streamelements-donation"
3687
+ }
3688
+ };
3689
+ var STREAMBOSS_LISTENER_KEYS = {
3690
+ twitch: {
3691
+ "follower-latest": "twitch-follower",
3692
+ "subscriber-latest": "twitch-subscriber",
3693
+ "tip-latest": "lumiastream-donation",
3694
+ "cheer-latest": "twitch-bits",
3695
+ "raid-latest": "twitch-raid"
3696
+ },
3697
+ kick: {
3698
+ "follower-latest": "kick-follower",
3699
+ "subscriber-latest": "kick-subscriber",
3700
+ "tip-latest": "lumiastream-donation",
3701
+ "cheer-latest": "kick-kicks",
3702
+ "raid-latest": "kick-host"
3703
+ },
3704
+ youtube: {
3705
+ "follower-latest": "youtube-subscriber",
3706
+ "subscriber-latest": "youtube-member",
3707
+ "tip-latest": "lumiastream-donation",
3708
+ "cheer-latest": "youtube-superchat"
3709
+ }
3564
3710
  };
3711
+ function alertBoxKeyFor(seEvent, provider = "twitch") {
3712
+ return ALERT_BOX_KEYS[provider]?.[seEvent];
3713
+ }
3714
+ function kappagenKeyFor(seEvent, provider = "twitch") {
3715
+ return KAPPAGEN_KEYS[provider]?.[seEvent];
3716
+ }
3717
+ function streamBossListenerKeyFor(seListener, provider = "twitch") {
3718
+ return STREAMBOSS_LISTENER_KEYS[provider]?.[seListener];
3719
+ }
3720
+ function readBootstrapProvider(bootstrap) {
3721
+ const ch = bootstrap.channel;
3722
+ if (!ch || typeof ch !== "object") return "twitch";
3723
+ const p = ch.provider;
3724
+ if (p === "kick" || p === "youtube" || p === "twitch") return p;
3725
+ return "twitch";
3726
+ }
3727
+
3728
+ // src/se-import/mappers/alert.ts
3565
3729
  function pickGraphics(g) {
3566
3730
  const DEFAULT_GRAPHIC_VOLUME = 0.5;
3567
3731
  if (!g) return { type: "image", content: { src: "", loop: false, volume: DEFAULT_GRAPHIC_VOLUME } };
@@ -3744,12 +3908,12 @@ function pickBaseAlertFields(event) {
3744
3908
  function mapAlertBox(widget, opts = {}, ctx) {
3745
3909
  const v = widget.variables ?? {};
3746
3910
  const events = {};
3747
- const eventKeys = opts.onlyEvents ?? ["follower", "subscriber", "tip", "cheer", "raid", "merch", "purchase", "charityCampaignDonation"];
3911
+ const eventKeys = opts.onlyEvents ?? ["follower", "subscriber", "tip", "cheer", "raid", "host", "merch", "purchase", "charityCampaignDonation"];
3748
3912
  let firstEvent = null;
3749
3913
  for (const seKey of eventKeys) {
3750
3914
  const ev = v[seKey];
3751
3915
  if (!ev || typeof ev !== "object") continue;
3752
- const lumiaAlert = SE_EVENT_TO_LUMIA_ALERT[seKey];
3916
+ const lumiaAlert = alertBoxKeyFor(seKey, ctx?.provider);
3753
3917
  if (!lumiaAlert) continue;
3754
3918
  const built = buildAlertEvent(ev, seKey);
3755
3919
  events[lumiaAlert] = built;
@@ -4597,61 +4761,8 @@ function mapMediaShare(widget, ctx) {
4597
4761
  }
4598
4762
  }, ctx);
4599
4763
  }
4600
- var SE_KAPPAGEN_EVENT_TO_LUMIA_ALERT = {
4601
- follower: "twitch-follower",
4602
- subscriber: "twitch-subscriber",
4603
- cheer: "twitch-bits",
4604
- raid: "twitch-raid",
4605
- tip: "streamelements-donation"
4606
- // NOTE — three SE events intentionally NOT mapped:
4607
- // • `host` → previously `twitch-host`, which doesn't exist in
4608
- // LumiaAlertValues (verified). Twitch deprecated hosting
4609
- // in 2022 so the event never fires anyway.
4610
- // • `purchase` → previously `streamlabs-merch`, which is deprecated. The
4611
- // main Alert Box mapper routes merch/purchase to
4612
- // `fourthwall-shopOrder`; keep Kappagen consistent by
4613
- // dropping the legacy SL path rather than splitting
4614
- // emoji emotion between two competing alert keys.
4615
- // • `embers` → previously `youtube-superchat`, which is wrong on both
4616
- // ends: embers were a deprecated Twitch paid-emote
4617
- // feature unrelated to YouTube super chats.
4618
- // All three drop silently; the rest of the widget (size, spawn zone,
4619
- // per-event flags for the remaining events) still imports.
4620
- };
4621
- function normalizeKappagenChatEmoteMode(mode) {
4622
- const normalized = String(mode ?? "").trim().toLowerCase();
4623
- if (!normalized || normalized === "off" || normalized === "false" || normalized === "none") return "off";
4624
- if (normalized.includes("combo")) return "combo";
4625
- return "show-all";
4626
- }
4627
4764
  function mapKappagen(widget, ctx) {
4628
4765
  const v = widget.variables ?? {};
4629
- const kappagenName = widget.name ?? widget.id ?? "unnamed widget";
4630
- console.groupCollapsed("[SE Import][Kappagen raw widget]", kappagenName);
4631
- console.log("Full StreamElements widget:", widget);
4632
- console.log("Raw variables/settings:", widget.variables ?? null);
4633
- console.log("Listeners:", widget.listeners ?? null);
4634
- console.log("Events:", v.events ?? null);
4635
- console.log("Events config:", v.eventsConfig ?? null);
4636
- console.log("Kappagen animation/source settings:", {
4637
- cheersplosions: v.cheersplosions,
4638
- emotesplosion: v.emotesplosion,
4639
- emotesplosiondir: v.emotesplosiondir,
4640
- emotesplosionduration: v.emotesplosionduration,
4641
- mode: v.mode,
4642
- size: v.size,
4643
- spawnzone: v.spawnzone,
4644
- gif: v.gif,
4645
- emoji: v.emoji,
4646
- bttv: v.bttv,
4647
- ffz: v.ffz,
4648
- sevenTv: v.sevenTv,
4649
- duration: v.duration,
4650
- direction: v.direction,
4651
- fullscreen: v.fullscreen,
4652
- limit: v.limit
4653
- });
4654
- console.groupEnd();
4655
4766
  const eventsFromNested = v.eventsConfig ? {
4656
4767
  subscriber: v.eventsConfig.subscriber?.enabled ?? true,
4657
4768
  follower: v.eventsConfig.follower?.enabled ?? true,
@@ -4660,10 +4771,9 @@ function mapKappagen(widget, ctx) {
4660
4771
  tip: v.eventsConfig.tip?.enabled ?? true
4661
4772
  } : null;
4662
4773
  const events = v.events ?? eventsFromNested ?? { subscriber: true, follower: true, cheer: true, raid: true, tip: true };
4663
- const chatEmoteMode = normalizeKappagenChatEmoteMode(v.mode);
4664
- const alertTriggers = Object.entries(events).filter(([, enabled]) => !!enabled).map(([seKey]) => SE_KAPPAGEN_EVENT_TO_LUMIA_ALERT[seKey]).filter((x) => !!x);
4774
+ const alertTriggers = Object.entries(events).filter(([, enabled]) => !!enabled).map(([seKey]) => kappagenKeyFor(seKey, ctx?.provider)).filter((x) => !!x);
4665
4775
  const alertEmotes = ["Kappa", "<3", "imGlitch"];
4666
- const unit = buildUnit(
4776
+ return buildUnit(
4667
4777
  widget,
4668
4778
  "emotealert",
4669
4779
  {
@@ -4683,9 +4793,6 @@ function mapKappagen(widget, ctx) {
4683
4793
  animationTime: typeof v.duration === "number" ? v.duration : 5,
4684
4794
  duration: typeof v.duration === "number" ? v.duration : 5,
4685
4795
  useAnimatedEmotes: true,
4686
- alertOn: true,
4687
- chatEmotesOn: chatEmoteMode !== "off",
4688
- chatEmoteMode,
4689
4796
  alertNumberEmotes: typeof v.limit === "number" && v.limit > 0 ? v.limit : 30,
4690
4797
  alertEmotes,
4691
4798
  alertTriggers,
@@ -4712,15 +4819,6 @@ function mapKappagen(widget, ctx) {
4712
4819
  },
4713
4820
  ctx
4714
4821
  );
4715
- console.groupCollapsed("[SE Import][Kappagen mapped emotealert]", kappagenName);
4716
- console.log("Resolved events:", events);
4717
- console.log("Mapped alert triggers:", alertTriggers);
4718
- console.log("Mapped chat emote mode:", chatEmoteMode);
4719
- console.log("Mapped alert emotes:", alertEmotes);
4720
- console.log("Mapped Emotealert unit:", unit);
4721
- console.log("Mapped Emotealert content:", unit.module?.content ?? null);
4722
- console.groupEnd();
4723
- return unit;
4724
4822
  }
4725
4823
  function mapLeaderboardFromVariables(widget, currency, ctx) {
4726
4824
  const v = widget.variables ?? {};
@@ -5063,16 +5161,7 @@ function parsePx(raw) {
5063
5161
  }
5064
5162
 
5065
5163
  // src/se-import/mappers/streamboss.ts
5066
- var SE_LISTENER_TO_ALERT = {
5067
- "follower-latest": "twitch-follower",
5068
- "subscriber-latest": "twitch-subscriber",
5069
- "tip-latest": "lumiastream-donation",
5070
- "cheer-latest": "twitch-bits",
5071
- "raid-latest": "twitch-raid"
5072
- // `purchase-latest` and `subscriber-gifted-latest` are intentionally absent.
5073
- // SE rolled gifted subs into `subscriber-latest`; Lumia splits them via
5074
- // extraSettings.isGift, which the runtime already handles consistently.
5075
- };
5164
+ var SE_LISTENERS = ["follower-latest", "subscriber-latest", "tip-latest", "cheer-latest", "raid-latest"];
5076
5165
  var SE_LISTENER_MODE = {
5077
5166
  "follower-latest": "flat",
5078
5167
  "subscriber-latest": "flat",
@@ -5094,8 +5183,10 @@ function mapStreamBoss(widget, ctx) {
5094
5183
  const seDamage = v.damage ?? {};
5095
5184
  const damageMap = {};
5096
5185
  const selectedEvents = [];
5097
- for (const [seListener, alertKey] of Object.entries(SE_LISTENER_TO_ALERT)) {
5186
+ for (const seListener of SE_LISTENERS) {
5098
5187
  if (!listenerIsOn(widget.listeners, seListener)) continue;
5188
+ const alertKey = streamBossListenerKeyFor(seListener, ctx?.provider);
5189
+ if (!alertKey) continue;
5099
5190
  const raw = seDamage[seListener];
5100
5191
  const value = typeof raw === "number" && Number.isFinite(raw) && raw > 0 ? raw : null;
5101
5192
  if (value === null) continue;
@@ -7230,6 +7321,7 @@ function SEImportWizard({
7230
7321
  existingAssets,
7231
7322
  saveOverlay,
7232
7323
  onComplete,
7324
+ onOverlayImported,
7233
7325
  onClose,
7234
7326
  notify,
7235
7327
  CustomEmbed
@@ -7728,7 +7820,15 @@ function SEImportWizard({
7728
7820
  notify.success("Imported overlay from StreamElements.");
7729
7821
  mirrorAbortRef.current?.abort();
7730
7822
  onClose();
7731
- if (uuid) onComplete({ overlayId: uuid });
7823
+ if (uuid) {
7824
+ onOverlayImported?.({
7825
+ overlayId: uuid,
7826
+ sourceSEOverlayName: result.overlay.name || void 0,
7827
+ width: result.overlay.settings.metadata.width || void 0,
7828
+ height: result.overlay.settings.metadata.height || void 0
7829
+ });
7830
+ onComplete({ overlayId: uuid });
7831
+ }
7732
7832
  } catch (error) {
7733
7833
  notify.error(`Import failed: ${extractErrorMessage(error)}`);
7734
7834
  } finally {
@@ -7744,6 +7844,18 @@ function SEImportWizard({
7744
7844
  settings: result.overlay.settings
7745
7845
  };
7746
7846
  const { uuid } = await saveOverlay(body);
7847
+ const orderedIds = Array.from(selectedOverlayIds);
7848
+ const sourceSEOverlayId = orderedIds[currentOverlayIndex - 1];
7849
+ const sourceSummary = sourceSEOverlayId ? (overlayList ?? []).find((o) => o._id === sourceSEOverlayId) : void 0;
7850
+ if (uuid) {
7851
+ onOverlayImported?.({
7852
+ overlayId: uuid,
7853
+ sourceSEOverlayId,
7854
+ sourceSEOverlayName: sourceSummary?.name ?? sourceSummary?.settings?.name ?? result.overlay.name ?? void 0,
7855
+ width: sourceSummary?.settings?.width ?? result.overlay.settings.metadata.width ?? void 0,
7856
+ height: sourceSummary?.settings?.height ?? result.overlay.settings.metadata.height ?? void 0
7857
+ });
7858
+ }
7747
7859
  const hasMore = overlayQueue.length > 0;
7748
7860
  if (hasMore) {
7749
7861
  notify.success(
@@ -7771,6 +7883,7 @@ function SEImportWizard({
7771
7883
  notify,
7772
7884
  onClose,
7773
7885
  onComplete,
7886
+ onOverlayImported,
7774
7887
  options.name,
7775
7888
  result,
7776
7889
  saveOverlay,
@@ -7778,7 +7891,9 @@ function SEImportWizard({
7778
7891
  overlayQueue,
7779
7892
  currentOverlayIndex,
7780
7893
  totalOverlaysToImport,
7781
- loadBootstrapAndKickoff
7894
+ loadBootstrapAndKickoff,
7895
+ selectedOverlayIds,
7896
+ overlayList
7782
7897
  ]);
7783
7898
  const visibleSteps = useMemo5(() => {
7784
7899
  const showMirrorStep = options.mirrorAssets && (assetUrls.length > 0 || mirrorRows.length > 0 || step === "mirror");
@@ -8156,18 +8271,20 @@ function importSEBootstrap(bootstrap) {
8156
8271
  mappings: [],
8157
8272
  notes: []
8158
8273
  };
8274
+ const provider = readBootstrapProvider(bootstrap);
8159
8275
  console.log("[SE-IMPORT] bootstrap received", {
8160
8276
  overlayId: seOverlay?._id,
8161
8277
  name: seOverlay?.name,
8162
8278
  canvas: { width: seOverlay?.settings?.width, height: seOverlay?.settings?.height },
8163
8279
  widgetCount: widgets.length,
8164
- widgetTypes: widgets.map((w) => w.type)
8280
+ widgetTypes: widgets.map((w) => w.type),
8281
+ provider
8165
8282
  });
8166
8283
  const importCanvas = {
8167
8284
  width: seOverlay?.settings?.width ?? 1920,
8168
8285
  height: seOverlay?.settings?.height ?? 1080
8169
8286
  };
8170
- const ctx = { canvas: importCanvas };
8287
+ const ctx = { canvas: importCanvas, provider };
8171
8288
  const layers = [];
8172
8289
  const modules = {};
8173
8290
  const reviewItems = [];
@@ -326,6 +326,13 @@ interface SEImportBindings {
326
326
  onComplete: (result: {
327
327
  overlayId: string;
328
328
  }) => void;
329
+ onOverlayImported?: (result: {
330
+ overlayId: string;
331
+ sourceSEOverlayId?: string;
332
+ sourceSEOverlayName?: string;
333
+ width?: number;
334
+ height?: number;
335
+ }) => void;
329
336
  onClose: () => void;
330
337
  notify: {
331
338
  success: (msg: string) => void;