@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/se-import.js CHANGED
@@ -16,45 +16,104 @@ var SE_TO_LUMIA_PLACEHOLDER = {
16
16
  total: "total",
17
17
  gifts: "giftAmount"
18
18
  };
19
- function listenerPlaceholderContext(listener) {
20
- if (listener === "follower-latest") return { name: "twitch_last_follower", username: "twitch_last_follower" };
21
- if (listener === "subscriber-latest" || listener === "subscriber-new-latest" || listener === "subscriber-resub-latest") {
22
- return { name: "twitch_last_subscriber", username: "twitch_last_subscriber" };
19
+ var TWITCH_CONTEXTS = {
20
+ "follower-latest": { name: "twitch_last_follower", username: "twitch_last_follower" },
21
+ "subscriber-latest": { name: "twitch_last_subscriber", username: "twitch_last_subscriber" },
22
+ "subscriber-new-latest": { name: "twitch_last_subscriber", username: "twitch_last_subscriber" },
23
+ "subscriber-resub-latest": { name: "twitch_last_subscriber", username: "twitch_last_subscriber" },
24
+ "subscriber-gifted-latest": { name: "twitch_last_gifter", username: "twitch_last_gifter", amount: "twitch_last_gifter_amount", count: "twitch_last_gifter_amount" },
25
+ "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" },
26
+ "cheer-latest": { name: "twitch_last_bit", username: "twitch_last_bit", amount: "twitch_last_bit_amount", count: "twitch_last_bit_amount" },
27
+ "tip-latest": { name: "latest_donator", username: "latest_donator", amount: "latest_donator_amount", count: "latest_donator_amount", currency: "latest_donator_currency_symbol" },
28
+ latest_donator: { name: "latest_donator", username: "latest_donator", amount: "latest_donator_amount", count: "latest_donator_amount", currency: "latest_donator_currency_symbol" },
29
+ "raid-latest": { name: "twitch_last_raider", username: "twitch_last_raider", amount: "twitch_last_raid_amount", count: "twitch_last_raid_amount" },
30
+ // Session counters: count/amount = the running session count.
31
+ "follower-session": { amount: "twitch_session_follower_count", count: "twitch_session_follower_count" },
32
+ "subscriber-session": { amount: "twitch_session_subscribers_count", count: "twitch_session_subscribers_count" },
33
+ "cheer-session": { amount: "twitch_session_bits_count", count: "twitch_session_bits_count" },
34
+ "tip-session": { amount: "session_donation_amount", count: "session_donation_count", currency: "session_donation_amount_currency_symbol" },
35
+ // Totals (all-time): count/amount = total counter.
36
+ "follower-total": { amount: "twitch_total_follower_count", count: "twitch_total_follower_count" },
37
+ "subscriber-total": { amount: "twitch_total_subscriber_count", count: "twitch_total_subscriber_count" },
38
+ "cheer-total": { amount: "twitch_total_bits_count", count: "twitch_total_bits_count" },
39
+ "tip-total": { amount: "total_donation_amount", count: "total_donation_amount", currency: "total_donation_amount_currency_symbol" },
40
+ "tip-count": { amount: "donation_count", count: "donation_count" },
41
+ // Top cheer / cheerer / donator session readouts.
42
+ "cheer-session-top-donation": { amount: "twitch_session_top_cheer_amount", count: "twitch_session_top_cheer_amount" },
43
+ "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" },
44
+ "tip-session-top-donation": { amount: "session_top_donator_amount", count: "session_top_donator_amount", currency: "session_top_donator_currency_symbol" },
45
+ "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" },
46
+ // Botcounter — SE uses `{wins}` for the running chat count; the dispatcher's
47
+ // fallbackVar for `se-widget-botcounter` is `twitch_session_chat_count`, so
48
+ // when the listener arg lands as that var name we treat it as the wins source.
49
+ twitch_session_chat_count: { wins: "twitch_session_chat_count", count: "twitch_session_chat_count", amount: "twitch_session_chat_count" },
50
+ // Merch readouts — SE used to ship via their own shop, now most users route
51
+ // through Fourthwall. Lumia exposes `fourthwall_last_order` as a full object:
52
+ // { username, email, friendlyId, amount, currency, message, items,
53
+ // itemsCount, itemsDetailed, orderItems, itemImages, firstItem }
54
+ // (see fourthwall.manager.ts ORDER_PLACED handler). The template engine
55
+ // supports dotted access (`dotNotationDig`) so we pluck the right field.
56
+ "merch-latest": {
57
+ name: "fourthwall_last_order.username",
58
+ username: "fourthwall_last_order.username",
59
+ amount: "fourthwall_last_order.amount",
60
+ count: "fourthwall_last_order.itemsCount",
61
+ currency: "fourthwall_last_order.currency",
62
+ items: "fourthwall_last_order.items",
63
+ item: "fourthwall_last_order.firstItem.name",
64
+ message: "fourthwall_last_order.message"
65
+ },
66
+ "purchase-latest": {
67
+ name: "fourthwall_last_order.username",
68
+ username: "fourthwall_last_order.username",
69
+ amount: "fourthwall_last_order.amount",
70
+ count: "fourthwall_last_order.itemsCount",
71
+ currency: "fourthwall_last_order.currency",
72
+ items: "fourthwall_last_order.items",
73
+ item: "fourthwall_last_order.firstItem.name",
74
+ message: "fourthwall_last_order.message"
23
75
  }
24
- if (listener === "subscriber-gifted-latest") return { name: "twitch_last_gifter", username: "twitch_last_gifter", amount: "twitch_last_gifter_amount", count: "twitch_last_gifter_amount" };
25
- 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" };
26
- if (listener === "cheer-latest") return { name: "twitch_last_bit", username: "twitch_last_bit", amount: "twitch_last_bit_amount", count: "twitch_last_bit_amount" };
27
- if (listener === "tip-latest" || listener === "latest_donator") {
28
- return { name: "latest_donator", username: "latest_donator", amount: "latest_donator_amount", count: "latest_donator_amount", currency: "latest_donator_currency_symbol" };
29
- }
30
- if (listener === "raid-latest") return { name: "twitch_last_raider", username: "twitch_last_raider", amount: "twitch_last_raid_amount", count: "twitch_last_raid_amount" };
31
- if (listener === "follower-session") return { amount: "twitch_session_follower_count", count: "twitch_session_follower_count" };
32
- if (listener === "subscriber-session") return { amount: "twitch_session_subscribers_count", count: "twitch_session_subscribers_count" };
33
- if (listener === "cheer-session") return { amount: "twitch_session_bits_count", count: "twitch_session_bits_count" };
34
- if (listener === "tip-session") return { amount: "session_donation_amount", count: "session_donation_count", currency: "session_donation_amount_currency_symbol" };
35
- if (listener === "follower-total") return { amount: "twitch_total_follower_count", count: "twitch_total_follower_count" };
36
- if (listener === "subscriber-total") return { amount: "twitch_total_subscriber_count", count: "twitch_total_subscriber_count" };
37
- if (listener === "cheer-total") return { amount: "twitch_total_bits_count", count: "twitch_total_bits_count" };
38
- if (listener === "tip-total") return { amount: "total_donation_amount", count: "total_donation_amount", currency: "total_donation_amount_currency_symbol" };
39
- if (listener === "tip-count") return { amount: "donation_count", count: "donation_count" };
40
- if (listener === "cheer-session-top-donation") return { amount: "twitch_session_top_cheer_amount", count: "twitch_session_top_cheer_amount" };
41
- 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" };
42
- if (listener === "tip-session-top-donation") return { amount: "session_top_donator_amount", count: "session_top_donator_amount", currency: "session_top_donator_currency_symbol" };
43
- 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" };
44
- if (listener === "twitch_session_chat_count") return { wins: "twitch_session_chat_count", count: "twitch_session_chat_count", amount: "twitch_session_chat_count" };
45
- if (listener === "merch-latest" || listener === "purchase-latest") {
46
- return {
47
- name: "fourthwall_last_order.username",
48
- username: "fourthwall_last_order.username",
49
- amount: "fourthwall_last_order.amount",
50
- count: "fourthwall_last_order.itemsCount",
51
- currency: "fourthwall_last_order.currency",
52
- items: "fourthwall_last_order.items",
53
- item: "fourthwall_last_order.firstItem.name",
54
- message: "fourthwall_last_order.message"
55
- };
56
- }
57
- return {};
76
+ };
77
+ var KICK_CONTEXTS = {
78
+ "follower-latest": { name: "kick_last_follower", username: "kick_last_follower" },
79
+ "subscriber-latest": { name: "kick_last_subscriber", username: "kick_last_subscriber" },
80
+ "subscriber-new-latest": { name: "kick_last_subscriber", username: "kick_last_subscriber" },
81
+ "subscriber-resub-latest": { name: "kick_last_subscriber", username: "kick_last_subscriber" },
82
+ "subscriber-gifted-latest": { name: "kick_last_gifter", username: "kick_last_gifter", amount: "kick_last_gifter_amount", count: "kick_last_gifter_amount" },
83
+ "cheer-latest": { name: "kick_last_kicks", username: "kick_last_kicks", amount: "kick_last_kicks_amount", count: "kick_last_kicks_amount" },
84
+ "raid-latest": { name: "kick_last_host", username: "kick_last_host", amount: "kick_last_host_amount", count: "kick_last_host_amount" },
85
+ "follower-session": { amount: "kick_session_follower_count", count: "kick_session_follower_count" },
86
+ "subscriber-session": { amount: "kick_session_subscriber_count", count: "kick_session_subscriber_count" },
87
+ "cheer-session": { amount: "kick_session_kicks_count", count: "kick_session_kicks_count" },
88
+ "subscriber-gifted-session": { amount: "kick_session_gifts_count", count: "kick_session_gifts_count" },
89
+ "follower-total": { amount: "kick_total_follower_count", count: "kick_total_follower_count" },
90
+ "subscriber-total": { amount: "kick_total_subscriber_count", count: "kick_total_subscriber_count" },
91
+ "cheer-total": { amount: "kick_total_kicks_count", count: "kick_total_kicks_count" },
92
+ "follower-week": { amount: "kick_week_follower_count", count: "kick_week_follower_count" },
93
+ "follower-month": { amount: "kick_month_follower_count", count: "kick_month_follower_count" },
94
+ "subscriber-week": { amount: "kick_week_subscriber_count", count: "kick_week_subscriber_count" },
95
+ "subscriber-month": { amount: "kick_month_subscriber_count", count: "kick_month_subscriber_count" }
96
+ };
97
+ var YOUTUBE_CONTEXTS = {
98
+ "follower-latest": { name: "youtube_last_subscriber", username: "youtube_last_subscriber" },
99
+ "subscriber-latest": { name: "youtube_last_member", username: "youtube_last_member" },
100
+ "subscriber-new-latest": { name: "youtube_last_member", username: "youtube_last_member" },
101
+ "subscriber-resub-latest": { name: "youtube_last_member", username: "youtube_last_member" },
102
+ "subscriber-gifted-latest": { name: "youtube_last_gift_member", username: "youtube_last_gift_member" },
103
+ "cheer-latest": { name: "youtube_last_superchatter", username: "youtube_last_superchatter" },
104
+ "follower-session": { amount: "youtube_session_subscriber_count", count: "youtube_session_subscriber_count" },
105
+ "subscriber-session": { amount: "youtube_session_member_count", count: "youtube_session_member_count" },
106
+ "cheer-session": { amount: "youtube_session_superchat_count", count: "youtube_session_superchat_count" },
107
+ "follower-total": { amount: "youtube_total_subscriber_count", count: "youtube_total_subscriber_count" },
108
+ "subscriber-total": { amount: "youtube_total_member_count", count: "youtube_total_member_count" },
109
+ "follower-week": { amount: "youtube_week_subscriber_count", count: "youtube_week_subscriber_count" },
110
+ "follower-month": { amount: "youtube_month_subscriber_count", count: "youtube_month_subscriber_count" },
111
+ "subscriber-week": { amount: "youtube_week_member_count", count: "youtube_week_member_count" },
112
+ "subscriber-month": { amount: "youtube_month_member_count", count: "youtube_month_member_count" }
113
+ };
114
+ function listenerPlaceholderContext(listener, provider = "twitch") {
115
+ const platformTable = provider === "kick" ? KICK_CONTEXTS : provider === "youtube" ? YOUTUBE_CONTEXTS : null;
116
+ return platformTable?.[listener] ?? TWITCH_CONTEXTS[listener] ?? {};
58
117
  }
59
118
  var SE_WIDGET_TYPE_LABELS = {
60
119
  "subscriber-latest": "Latest subscriber",
@@ -90,17 +149,57 @@ function defaultLabelForSEWidget(widget) {
90
149
  const tail = words.slice(1).join(" ");
91
150
  return tail ? `${head} ${tail}` : head;
92
151
  }
93
- function translateSeText(input, listener, passthrough) {
152
+ function translateSeText(input, listener, passthrough, provider) {
94
153
  if (!input) return "";
95
- const ctx = listener ? listenerPlaceholderContext(listener) : {};
154
+ const ctx = listener ? listenerPlaceholderContext(listener, provider) : {};
96
155
  return input.replace(/\{([a-zA-Z][a-zA-Z0-9_.]*)\}/g, (_match, raw) => {
97
156
  if (passthrough?.has(raw)) return `{{${raw}}}`;
98
157
  const mapped = ctx[raw] ?? SE_TO_LUMIA_PLACEHOLDER[raw] ?? raw;
99
158
  return `{{${mapped}}}`;
100
159
  });
101
160
  }
102
- function listenerToLumiaVariable(listener) {
161
+ var KICK_LISTENER_VARS = {
162
+ "follower-latest": "kick_last_follower",
163
+ "subscriber-latest": "kick_last_subscriber",
164
+ "subscriber-new-latest": "kick_last_subscriber",
165
+ "subscriber-resub-latest": "kick_last_subscriber",
166
+ "subscriber-gifted-latest": "kick_last_gifter",
167
+ "cheer-latest": "kick_last_kicks",
168
+ "raid-latest": "kick_last_host",
169
+ "follower-session": "kick_session_follower_count",
170
+ "follower-week": "kick_week_follower_count",
171
+ "follower-month": "kick_month_follower_count",
172
+ "follower-total": "kick_total_follower_count",
173
+ "subscriber-session": "kick_session_subscriber_count",
174
+ "subscriber-week": "kick_week_subscriber_count",
175
+ "subscriber-month": "kick_month_subscriber_count",
176
+ "subscriber-total": "kick_total_subscriber_count",
177
+ "subscriber-gifted-session": "kick_session_gifts_count",
178
+ "cheer-session": "kick_session_kicks_count",
179
+ "cheer-total": "kick_total_kicks_count"
180
+ };
181
+ var YOUTUBE_LISTENER_VARS = {
182
+ "follower-latest": "youtube_last_subscriber",
183
+ "subscriber-latest": "youtube_last_member",
184
+ "subscriber-new-latest": "youtube_last_member",
185
+ "subscriber-resub-latest": "youtube_last_member",
186
+ "subscriber-gifted-latest": "youtube_last_gift_member",
187
+ "cheer-latest": "youtube_last_superchatter",
188
+ "follower-session": "youtube_session_subscriber_count",
189
+ "follower-week": "youtube_week_subscriber_count",
190
+ "follower-month": "youtube_month_subscriber_count",
191
+ "follower-total": "youtube_total_subscriber_count",
192
+ "subscriber-session": "youtube_session_member_count",
193
+ "subscriber-week": "youtube_week_member_count",
194
+ "subscriber-month": "youtube_month_member_count",
195
+ "subscriber-total": "youtube_total_member_count",
196
+ "subscriber-gifted-session": "youtube_session_gift_members_count",
197
+ "cheer-session": "youtube_session_superchat_count"
198
+ };
199
+ function listenerToLumiaVariable(listener, provider = "twitch") {
103
200
  if (typeof listener !== "string" || !listener) return "username";
201
+ if (provider === "kick" && KICK_LISTENER_VARS[listener]) return KICK_LISTENER_VARS[listener];
202
+ if (provider === "youtube" && YOUTUBE_LISTENER_VARS[listener]) return YOUTUBE_LISTENER_VARS[listener];
104
203
  const m = {
105
204
  // Latest events
106
205
  "follower-latest": "twitch_last_follower",
@@ -398,10 +497,10 @@ function mapReadout(widget, fallbackVar, ctx) {
398
497
  const explicit = typeof explicitListener === "string" ? explicitListener : null;
399
498
  const widgetListener = typeof widget.listener === "string" ? widget.listener : null;
400
499
  const listener = explicit ?? widgetListener ?? fallbackVar;
401
- const variable = listenerToLumiaVariable(listener);
500
+ const variable = listenerToLumiaVariable(listener, ctx?.provider);
402
501
  const userTemplate = widget.text?.value ?? "";
403
502
  const inheritsText = userTemplate.trim().length > 0 && /\{[a-zA-Z]/.test(userTemplate);
404
- const value = inheritsText ? translateSeText(userTemplate, listener) : `{{${variable}}}`;
503
+ const value = inheritsText ? translateSeText(userTemplate, listener, void 0, ctx?.provider) : `{{${variable}}}`;
405
504
  const seCss = widget.text?.css ?? {};
406
505
  return buildUnit(widget, "text", {
407
506
  content: { value, highlightColor: "inherit" },
@@ -419,34 +518,99 @@ function mapReadout(widget, fallbackVar, ctx) {
419
518
  }, ctx);
420
519
  }
421
520
 
422
- // src/se-import/mappers/alert.ts
423
- var SE_EVENT_TO_LUMIA_ALERT = {
424
- follower: "twitch-follower",
425
- subscriber: "twitch-subscriber",
426
- tip: "streamelements-donation",
427
- cheer: "twitch-bits",
428
- raid: "twitch-raid",
429
- // Merch / shop-order — Lumia's Fourthwall integration is the data path most
430
- // users now have wired up since SE deprecated their own merch flow in favor
431
- // of Fourthwall. `fourthwall-shopOrder` is the alert key from
432
- // LumiaAlertValues / activity.types.ts.
433
- merch: "fourthwall-shopOrder",
434
- purchase: "fourthwall-shopOrder",
435
- charityCampaignDonation: "twitch-charityDonation"
436
- // NOTE — two SE events intentionally NOT mapped:
437
- // • `host` → previously `twitch-raid`. Twitch deprecated hosting in
438
- // late 2022 so the event never fires on the platform anymore. Even
439
- // if it did, host payloads don't carry a viewer count (raids do),
440
- // so the rendered alert would read "{name} raided with ?? viewers".
441
- // Drop the mapping; SE widgets configured with a `host` block fall
442
- // through to the unsupported placeholder rather than producing
443
- // misleading raid alerts.
444
- // • `redemption` → SE's `redemption` event is the legacy SE Store
445
- // redemption (their own loyalty-point store purchases), NOT Twitch
446
- // channel-point redemptions. Different payload, different semantics.
447
- // SE Store is mostly deprecated and seldom used; dropping the
448
- // mapping prevents misleading "channel-point redemption" alerts.
521
+ // src/se-import/mappers/provider-alerts.ts
522
+ var ALERT_BOX_KEYS = {
523
+ twitch: {
524
+ follower: "twitch-follower",
525
+ subscriber: "twitch-subscriber",
526
+ tip: "streamelements-donation",
527
+ cheer: "twitch-bits",
528
+ raid: "twitch-raid",
529
+ merch: "fourthwall-shopOrder",
530
+ purchase: "fourthwall-shopOrder",
531
+ charityCampaignDonation: "twitch-charityDonation"
532
+ },
533
+ kick: {
534
+ follower: "kick-follower",
535
+ subscriber: "kick-subscriber",
536
+ tip: "streamelements-donation",
537
+ cheer: "kick-kicks",
538
+ raid: "kick-host",
539
+ host: "kick-host",
540
+ merch: "fourthwall-shopOrder",
541
+ purchase: "fourthwall-shopOrder"
542
+ },
543
+ youtube: {
544
+ follower: "youtube-subscriber",
545
+ subscriber: "youtube-member",
546
+ tip: "streamelements-donation",
547
+ cheer: "youtube-superchat",
548
+ merch: "fourthwall-shopOrder",
549
+ purchase: "fourthwall-shopOrder"
550
+ }
551
+ };
552
+ var KAPPAGEN_KEYS = {
553
+ twitch: {
554
+ follower: "twitch-follower",
555
+ subscriber: "twitch-subscriber",
556
+ cheer: "twitch-bits",
557
+ raid: "twitch-raid",
558
+ tip: "streamelements-donation"
559
+ },
560
+ kick: {
561
+ follower: "kick-follower",
562
+ subscriber: "kick-subscriber",
563
+ cheer: "kick-kicks",
564
+ raid: "kick-host",
565
+ tip: "streamelements-donation"
566
+ },
567
+ youtube: {
568
+ follower: "youtube-subscriber",
569
+ subscriber: "youtube-member",
570
+ cheer: "youtube-superchat",
571
+ tip: "streamelements-donation"
572
+ }
573
+ };
574
+ var STREAMBOSS_LISTENER_KEYS = {
575
+ twitch: {
576
+ "follower-latest": "twitch-follower",
577
+ "subscriber-latest": "twitch-subscriber",
578
+ "tip-latest": "lumiastream-donation",
579
+ "cheer-latest": "twitch-bits",
580
+ "raid-latest": "twitch-raid"
581
+ },
582
+ kick: {
583
+ "follower-latest": "kick-follower",
584
+ "subscriber-latest": "kick-subscriber",
585
+ "tip-latest": "lumiastream-donation",
586
+ "cheer-latest": "kick-kicks",
587
+ "raid-latest": "kick-host"
588
+ },
589
+ youtube: {
590
+ "follower-latest": "youtube-subscriber",
591
+ "subscriber-latest": "youtube-member",
592
+ "tip-latest": "lumiastream-donation",
593
+ "cheer-latest": "youtube-superchat"
594
+ }
449
595
  };
596
+ function alertBoxKeyFor(seEvent, provider = "twitch") {
597
+ return ALERT_BOX_KEYS[provider]?.[seEvent];
598
+ }
599
+ function kappagenKeyFor(seEvent, provider = "twitch") {
600
+ return KAPPAGEN_KEYS[provider]?.[seEvent];
601
+ }
602
+ function streamBossListenerKeyFor(seListener, provider = "twitch") {
603
+ return STREAMBOSS_LISTENER_KEYS[provider]?.[seListener];
604
+ }
605
+ function readBootstrapProvider(bootstrap) {
606
+ const ch = bootstrap.channel;
607
+ if (!ch || typeof ch !== "object") return "twitch";
608
+ const p = ch.provider;
609
+ if (p === "kick" || p === "youtube" || p === "twitch") return p;
610
+ return "twitch";
611
+ }
612
+
613
+ // src/se-import/mappers/alert.ts
450
614
  function pickGraphics(g) {
451
615
  const DEFAULT_GRAPHIC_VOLUME = 0.5;
452
616
  if (!g) return { type: "image", content: { src: "", loop: false, volume: DEFAULT_GRAPHIC_VOLUME } };
@@ -629,12 +793,12 @@ function pickBaseAlertFields(event) {
629
793
  function mapAlertBox(widget, opts = {}, ctx) {
630
794
  const v = widget.variables ?? {};
631
795
  const events = {};
632
- const eventKeys = opts.onlyEvents ?? ["follower", "subscriber", "tip", "cheer", "raid", "merch", "purchase", "charityCampaignDonation"];
796
+ const eventKeys = opts.onlyEvents ?? ["follower", "subscriber", "tip", "cheer", "raid", "host", "merch", "purchase", "charityCampaignDonation"];
633
797
  let firstEvent = null;
634
798
  for (const seKey of eventKeys) {
635
799
  const ev = v[seKey];
636
800
  if (!ev || typeof ev !== "object") continue;
637
- const lumiaAlert = SE_EVENT_TO_LUMIA_ALERT[seKey];
801
+ const lumiaAlert = alertBoxKeyFor(seKey, ctx?.provider);
638
802
  if (!lumiaAlert) continue;
639
803
  const built = buildAlertEvent(ev, seKey);
640
804
  events[lumiaAlert] = built;
@@ -1482,61 +1646,8 @@ function mapMediaShare(widget, ctx) {
1482
1646
  }
1483
1647
  }, ctx);
1484
1648
  }
1485
- var SE_KAPPAGEN_EVENT_TO_LUMIA_ALERT = {
1486
- follower: "twitch-follower",
1487
- subscriber: "twitch-subscriber",
1488
- cheer: "twitch-bits",
1489
- raid: "twitch-raid",
1490
- tip: "streamelements-donation"
1491
- // NOTE — three SE events intentionally NOT mapped:
1492
- // • `host` → previously `twitch-host`, which doesn't exist in
1493
- // LumiaAlertValues (verified). Twitch deprecated hosting
1494
- // in 2022 so the event never fires anyway.
1495
- // • `purchase` → previously `streamlabs-merch`, which is deprecated. The
1496
- // main Alert Box mapper routes merch/purchase to
1497
- // `fourthwall-shopOrder`; keep Kappagen consistent by
1498
- // dropping the legacy SL path rather than splitting
1499
- // emoji emotion between two competing alert keys.
1500
- // • `embers` → previously `youtube-superchat`, which is wrong on both
1501
- // ends: embers were a deprecated Twitch paid-emote
1502
- // feature unrelated to YouTube super chats.
1503
- // All three drop silently; the rest of the widget (size, spawn zone,
1504
- // per-event flags for the remaining events) still imports.
1505
- };
1506
- function normalizeKappagenChatEmoteMode(mode) {
1507
- const normalized = String(mode ?? "").trim().toLowerCase();
1508
- if (!normalized || normalized === "off" || normalized === "false" || normalized === "none") return "off";
1509
- if (normalized.includes("combo")) return "combo";
1510
- return "show-all";
1511
- }
1512
1649
  function mapKappagen(widget, ctx) {
1513
1650
  const v = widget.variables ?? {};
1514
- const kappagenName = widget.name ?? widget.id ?? "unnamed widget";
1515
- console.groupCollapsed("[SE Import][Kappagen raw widget]", kappagenName);
1516
- console.log("Full StreamElements widget:", widget);
1517
- console.log("Raw variables/settings:", widget.variables ?? null);
1518
- console.log("Listeners:", widget.listeners ?? null);
1519
- console.log("Events:", v.events ?? null);
1520
- console.log("Events config:", v.eventsConfig ?? null);
1521
- console.log("Kappagen animation/source settings:", {
1522
- cheersplosions: v.cheersplosions,
1523
- emotesplosion: v.emotesplosion,
1524
- emotesplosiondir: v.emotesplosiondir,
1525
- emotesplosionduration: v.emotesplosionduration,
1526
- mode: v.mode,
1527
- size: v.size,
1528
- spawnzone: v.spawnzone,
1529
- gif: v.gif,
1530
- emoji: v.emoji,
1531
- bttv: v.bttv,
1532
- ffz: v.ffz,
1533
- sevenTv: v.sevenTv,
1534
- duration: v.duration,
1535
- direction: v.direction,
1536
- fullscreen: v.fullscreen,
1537
- limit: v.limit
1538
- });
1539
- console.groupEnd();
1540
1651
  const eventsFromNested = v.eventsConfig ? {
1541
1652
  subscriber: v.eventsConfig.subscriber?.enabled ?? true,
1542
1653
  follower: v.eventsConfig.follower?.enabled ?? true,
@@ -1545,10 +1656,9 @@ function mapKappagen(widget, ctx) {
1545
1656
  tip: v.eventsConfig.tip?.enabled ?? true
1546
1657
  } : null;
1547
1658
  const events = v.events ?? eventsFromNested ?? { subscriber: true, follower: true, cheer: true, raid: true, tip: true };
1548
- const chatEmoteMode = normalizeKappagenChatEmoteMode(v.mode);
1549
- const alertTriggers = Object.entries(events).filter(([, enabled]) => !!enabled).map(([seKey]) => SE_KAPPAGEN_EVENT_TO_LUMIA_ALERT[seKey]).filter((x) => !!x);
1659
+ const alertTriggers = Object.entries(events).filter(([, enabled]) => !!enabled).map(([seKey]) => kappagenKeyFor(seKey, ctx?.provider)).filter((x) => !!x);
1550
1660
  const alertEmotes = ["Kappa", "<3", "imGlitch"];
1551
- const unit = buildUnit(
1661
+ return buildUnit(
1552
1662
  widget,
1553
1663
  "emotealert",
1554
1664
  {
@@ -1568,9 +1678,6 @@ function mapKappagen(widget, ctx) {
1568
1678
  animationTime: typeof v.duration === "number" ? v.duration : 5,
1569
1679
  duration: typeof v.duration === "number" ? v.duration : 5,
1570
1680
  useAnimatedEmotes: true,
1571
- alertOn: true,
1572
- chatEmotesOn: chatEmoteMode !== "off",
1573
- chatEmoteMode,
1574
1681
  alertNumberEmotes: typeof v.limit === "number" && v.limit > 0 ? v.limit : 30,
1575
1682
  alertEmotes,
1576
1683
  alertTriggers,
@@ -1597,15 +1704,6 @@ function mapKappagen(widget, ctx) {
1597
1704
  },
1598
1705
  ctx
1599
1706
  );
1600
- console.groupCollapsed("[SE Import][Kappagen mapped emotealert]", kappagenName);
1601
- console.log("Resolved events:", events);
1602
- console.log("Mapped alert triggers:", alertTriggers);
1603
- console.log("Mapped chat emote mode:", chatEmoteMode);
1604
- console.log("Mapped alert emotes:", alertEmotes);
1605
- console.log("Mapped Emotealert unit:", unit);
1606
- console.log("Mapped Emotealert content:", unit.module?.content ?? null);
1607
- console.groupEnd();
1608
- return unit;
1609
1707
  }
1610
1708
  function mapLeaderboardFromVariables(widget, currency, ctx) {
1611
1709
  const v = widget.variables ?? {};
@@ -1948,16 +2046,7 @@ function parsePx(raw) {
1948
2046
  }
1949
2047
 
1950
2048
  // src/se-import/mappers/streamboss.ts
1951
- var SE_LISTENER_TO_ALERT = {
1952
- "follower-latest": "twitch-follower",
1953
- "subscriber-latest": "twitch-subscriber",
1954
- "tip-latest": "lumiastream-donation",
1955
- "cheer-latest": "twitch-bits",
1956
- "raid-latest": "twitch-raid"
1957
- // `purchase-latest` and `subscriber-gifted-latest` are intentionally absent.
1958
- // SE rolled gifted subs into `subscriber-latest`; Lumia splits them via
1959
- // extraSettings.isGift, which the runtime already handles consistently.
1960
- };
2049
+ var SE_LISTENERS = ["follower-latest", "subscriber-latest", "tip-latest", "cheer-latest", "raid-latest"];
1961
2050
  var SE_LISTENER_MODE = {
1962
2051
  "follower-latest": "flat",
1963
2052
  "subscriber-latest": "flat",
@@ -1979,8 +2068,10 @@ function mapStreamBoss(widget, ctx) {
1979
2068
  const seDamage = v.damage ?? {};
1980
2069
  const damageMap = {};
1981
2070
  const selectedEvents = [];
1982
- for (const [seListener, alertKey] of Object.entries(SE_LISTENER_TO_ALERT)) {
2071
+ for (const seListener of SE_LISTENERS) {
1983
2072
  if (!listenerIsOn(widget.listeners, seListener)) continue;
2073
+ const alertKey = streamBossListenerKeyFor(seListener, ctx?.provider);
2074
+ if (!alertKey) continue;
1984
2075
  const raw = seDamage[seListener];
1985
2076
  const value = typeof raw === "number" && Number.isFinite(raw) && raw > 0 ? raw : null;
1986
2077
  if (value === null) continue;
@@ -6100,6 +6191,7 @@ function SEImportWizard({
6100
6191
  existingAssets,
6101
6192
  saveOverlay,
6102
6193
  onComplete,
6194
+ onOverlayImported,
6103
6195
  onClose,
6104
6196
  notify,
6105
6197
  CustomEmbed
@@ -6598,7 +6690,15 @@ function SEImportWizard({
6598
6690
  notify.success("Imported overlay from StreamElements.");
6599
6691
  mirrorAbortRef.current?.abort();
6600
6692
  onClose();
6601
- if (uuid) onComplete({ overlayId: uuid });
6693
+ if (uuid) {
6694
+ onOverlayImported?.({
6695
+ overlayId: uuid,
6696
+ sourceSEOverlayName: result.overlay.name || void 0,
6697
+ width: result.overlay.settings.metadata.width || void 0,
6698
+ height: result.overlay.settings.metadata.height || void 0
6699
+ });
6700
+ onComplete({ overlayId: uuid });
6701
+ }
6602
6702
  } catch (error) {
6603
6703
  notify.error(`Import failed: ${extractErrorMessage(error)}`);
6604
6704
  } finally {
@@ -6614,6 +6714,18 @@ function SEImportWizard({
6614
6714
  settings: result.overlay.settings
6615
6715
  };
6616
6716
  const { uuid } = await saveOverlay(body);
6717
+ const orderedIds = Array.from(selectedOverlayIds);
6718
+ const sourceSEOverlayId = orderedIds[currentOverlayIndex - 1];
6719
+ const sourceSummary = sourceSEOverlayId ? (overlayList ?? []).find((o) => o._id === sourceSEOverlayId) : void 0;
6720
+ if (uuid) {
6721
+ onOverlayImported?.({
6722
+ overlayId: uuid,
6723
+ sourceSEOverlayId,
6724
+ sourceSEOverlayName: sourceSummary?.name ?? sourceSummary?.settings?.name ?? result.overlay.name ?? void 0,
6725
+ width: sourceSummary?.settings?.width ?? result.overlay.settings.metadata.width ?? void 0,
6726
+ height: sourceSummary?.settings?.height ?? result.overlay.settings.metadata.height ?? void 0
6727
+ });
6728
+ }
6617
6729
  const hasMore = overlayQueue.length > 0;
6618
6730
  if (hasMore) {
6619
6731
  notify.success(
@@ -6641,6 +6753,7 @@ function SEImportWizard({
6641
6753
  notify,
6642
6754
  onClose,
6643
6755
  onComplete,
6756
+ onOverlayImported,
6644
6757
  options.name,
6645
6758
  result,
6646
6759
  saveOverlay,
@@ -6648,7 +6761,9 @@ function SEImportWizard({
6648
6761
  overlayQueue,
6649
6762
  currentOverlayIndex,
6650
6763
  totalOverlaysToImport,
6651
- loadBootstrapAndKickoff
6764
+ loadBootstrapAndKickoff,
6765
+ selectedOverlayIds,
6766
+ overlayList
6652
6767
  ]);
6653
6768
  const visibleSteps = useMemo5(() => {
6654
6769
  const showMirrorStep = options.mirrorAssets && (assetUrls.length > 0 || mirrorRows.length > 0 || step === "mirror");
@@ -7026,18 +7141,20 @@ function importSEBootstrap(bootstrap) {
7026
7141
  mappings: [],
7027
7142
  notes: []
7028
7143
  };
7144
+ const provider = readBootstrapProvider(bootstrap);
7029
7145
  console.log("[SE-IMPORT] bootstrap received", {
7030
7146
  overlayId: seOverlay?._id,
7031
7147
  name: seOverlay?.name,
7032
7148
  canvas: { width: seOverlay?.settings?.width, height: seOverlay?.settings?.height },
7033
7149
  widgetCount: widgets.length,
7034
- widgetTypes: widgets.map((w) => w.type)
7150
+ widgetTypes: widgets.map((w) => w.type),
7151
+ provider
7035
7152
  });
7036
7153
  const importCanvas = {
7037
7154
  width: seOverlay?.settings?.width ?? 1920,
7038
7155
  height: seOverlay?.settings?.height ?? 1080
7039
7156
  };
7040
- const ctx = { canvas: importCanvas };
7157
+ const ctx = { canvas: importCanvas, provider };
7041
7158
  const layers = [];
7042
7159
  const modules = {};
7043
7160
  const reviewItems = [];