@lumiastream/ui 0.2.8-alpha.26 → 0.2.8-alpha.28
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 +216 -50
- package/dist/se-import.js +216 -50
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3133,31 +3133,86 @@ var SE_TO_LUMIA_PLACEHOLDER = {
|
|
|
3133
3133
|
};
|
|
3134
3134
|
var TWITCH_CONTEXTS = {
|
|
3135
3135
|
"follower-latest": { name: "twitch_last_follower", username: "twitch_last_follower" },
|
|
3136
|
+
// Three SE listeners with genuinely distinct semantics — preserve the
|
|
3137
|
+
// distinction in the variable mapping so a widget configured for "Latest
|
|
3138
|
+
// new subscriber" doesn't render the latest *resub* (and vice versa).
|
|
3139
|
+
// `subscriber-latest` is the umbrella (anyone subscribing); the other two
|
|
3140
|
+
// are subtype-specific. The dedicated TWITCH_LAST_{NEW_SUBSCRIBER,
|
|
3141
|
+
// RESUBSCRIBER} variables are written by the Twitch event pipeline on
|
|
3142
|
+
// the matching sub.type event.
|
|
3136
3143
|
"subscriber-latest": { name: "twitch_last_subscriber", username: "twitch_last_subscriber" },
|
|
3137
|
-
"subscriber-new-latest": { name: "
|
|
3138
|
-
"subscriber-resub-latest": { name: "
|
|
3139
|
-
|
|
3144
|
+
"subscriber-new-latest": { name: "twitch_last_new_subscriber", username: "twitch_last_new_subscriber" },
|
|
3145
|
+
"subscriber-resub-latest": { name: "twitch_last_resubscriber", username: "twitch_last_resubscriber" },
|
|
3146
|
+
// `subscriber-gifted-latest`: SE uses `{sender}` for the giver and `{name}` for
|
|
3147
|
+
// the recipient (e.g. "{sender} gifted to {name}"). The two roles must point
|
|
3148
|
+
// at distinct Lumia variables — collapsing both onto `twitch_last_gifter`
|
|
3149
|
+
// rendered "Mr.Beast gifted to Mr.Beast" while the actual recipient was
|
|
3150
|
+
// nowhere. `sender` keeps the gifter; `name` / `username` switch to the
|
|
3151
|
+
// dedicated recipient variable `twitch_last_gifted`. Amount stays on the
|
|
3152
|
+
// gifter (it's the count of subs in that drop, owned by the gifter).
|
|
3153
|
+
"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" },
|
|
3140
3154
|
"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
3155
|
"cheer-latest": { name: "twitch_last_bit", username: "twitch_last_bit", amount: "twitch_last_bit_amount", count: "twitch_last_bit_amount" },
|
|
3142
3156
|
"tip-latest": { name: "latest_donator", username: "latest_donator", amount: "latest_donator_amount", count: "latest_donator_amount", currency: "latest_donator_currency_symbol" },
|
|
3143
3157
|
latest_donator: { name: "latest_donator", username: "latest_donator", amount: "latest_donator_amount", count: "latest_donator_amount", currency: "latest_donator_currency_symbol" },
|
|
3144
3158
|
"raid-latest": { name: "twitch_last_raider", username: "twitch_last_raider", amount: "twitch_last_raid_amount", count: "twitch_last_raid_amount" },
|
|
3145
|
-
//
|
|
3159
|
+
// Period counters: count/amount = the running counter for that window.
|
|
3160
|
+
// SE lets the streamer pick session / week / month / total on the same widget
|
|
3161
|
+
// type; the chosen period lands in `widget.listener` (e.g. `cheer-week`).
|
|
3162
|
+
// Each entry below mirrors a row in the Lumia variables table — the previous
|
|
3163
|
+
// table only carried session+total and broke any widget configured for
|
|
3164
|
+
// week/month because `{amount}` fell through to the unscoped placeholder
|
|
3165
|
+
// and rendered blank.
|
|
3146
3166
|
"follower-session": { amount: "twitch_session_follower_count", count: "twitch_session_follower_count" },
|
|
3147
|
-
"
|
|
3148
|
-
"
|
|
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.
|
|
3167
|
+
"follower-week": { amount: "twitch_week_follower_count", count: "twitch_week_follower_count" },
|
|
3168
|
+
"follower-month": { amount: "twitch_month_follower_count", count: "twitch_month_follower_count" },
|
|
3151
3169
|
"follower-total": { amount: "twitch_total_follower_count", count: "twitch_total_follower_count" },
|
|
3170
|
+
"subscriber-session": { amount: "twitch_session_subscribers_count", count: "twitch_session_subscribers_count" },
|
|
3171
|
+
"subscriber-week": { amount: "twitch_week_subscriber_count", count: "twitch_week_subscriber_count" },
|
|
3172
|
+
"subscriber-month": { amount: "twitch_month_subscriber_count", count: "twitch_month_subscriber_count" },
|
|
3152
3173
|
"subscriber-total": { amount: "twitch_total_subscriber_count", count: "twitch_total_subscriber_count" },
|
|
3174
|
+
"subscriber-new-session": { amount: "twitch_session_new_subscribers_count", count: "twitch_session_new_subscribers_count" },
|
|
3175
|
+
"subscriber-resub-session": { amount: "twitch_session_resub_subscribers_count", count: "twitch_session_resub_subscribers_count" },
|
|
3176
|
+
"subscriber-gifted-session": { amount: "twitch_session_gifted_subscribers_count", count: "twitch_session_gifted_subscribers_count" },
|
|
3177
|
+
"cheer-session": { amount: "twitch_session_bits_count", count: "twitch_session_bits_count" },
|
|
3178
|
+
"cheer-week": { amount: "twitch_week_bits_count", count: "twitch_week_bits_count" },
|
|
3179
|
+
"cheer-month": { amount: "twitch_month_bits_count", count: "twitch_month_bits_count" },
|
|
3153
3180
|
"cheer-total": { amount: "twitch_total_bits_count", count: "twitch_total_bits_count" },
|
|
3181
|
+
"tip-session": { amount: "session_donation_amount", count: "session_donation_count", currency: "session_donation_amount_currency_symbol" },
|
|
3182
|
+
"tip-week": { amount: "week_donation_amount", count: "week_donation_count", currency: "week_donation_amount_currency_symbol" },
|
|
3183
|
+
"tip-month": { amount: "month_donation_amount", count: "month_donation_count", currency: "month_donation_amount_currency_symbol" },
|
|
3154
3184
|
"tip-total": { amount: "total_donation_amount", count: "total_donation_amount", currency: "total_donation_amount_currency_symbol" },
|
|
3155
3185
|
"tip-count": { amount: "donation_count", count: "donation_count" },
|
|
3156
|
-
// Top cheer / cheerer / donator
|
|
3157
|
-
|
|
3186
|
+
// Top cheer / cheerer / donator readouts — per period. SE's `se-widget-top-*`
|
|
3187
|
+
// widgets encode the chosen period (session / weekly / monthly / alltime) in
|
|
3188
|
+
// the widget's `listener` field. Without these entries the user's `{amount}`
|
|
3189
|
+
// / `{name}` template tokens fall through to the unscoped placeholder map
|
|
3190
|
+
// (`{{amount}}` only resolves inside alert variations) and render blank.
|
|
3191
|
+
//
|
|
3192
|
+
// `*-top-donation` and `*-top-donator` are paired: SE nominally treats them
|
|
3193
|
+
// as "top single event" vs "top cumulative contributor", but the data
|
|
3194
|
+
// rendered for both lives in the same Lumia variable pair (`*_top_donator`
|
|
3195
|
+
// / `*_top_donator_amount`, `twitch_*_top_cheerer` / `..._amount`). SE
|
|
3196
|
+
// widgets routinely use `{name}` against the `-donation` listener to mean
|
|
3197
|
+
// "the user who made the top donation", so the two rows below carry the
|
|
3198
|
+
// same name/username mapping — anything else falls through to the unscoped
|
|
3199
|
+
// `{{username}}` and renders blank.
|
|
3200
|
+
"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" },
|
|
3158
3201
|
"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
|
-
"
|
|
3202
|
+
"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" },
|
|
3203
|
+
"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" },
|
|
3204
|
+
"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" },
|
|
3205
|
+
"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" },
|
|
3206
|
+
"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" },
|
|
3207
|
+
"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" },
|
|
3208
|
+
"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" },
|
|
3160
3209
|
"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" },
|
|
3210
|
+
"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" },
|
|
3211
|
+
"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" },
|
|
3212
|
+
"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" },
|
|
3213
|
+
"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" },
|
|
3214
|
+
"tip-alltime-top-donation": { name: "top_donator", username: "top_donator", amount: "top_donator_amount", count: "top_donator_amount", currency: "top_donator_currency_symbol" },
|
|
3215
|
+
"tip-alltime-top-donator": { name: "top_donator", username: "top_donator", amount: "top_donator_amount", count: "top_donator_amount", currency: "top_donator_currency_symbol" },
|
|
3161
3216
|
// Botcounter — SE uses `{wins}` for the running chat count; the dispatcher's
|
|
3162
3217
|
// fallbackVar for `se-widget-botcounter` is `twitch_session_chat_count`, so
|
|
3163
3218
|
// when the listener arg lands as that var name we treat it as the wins source.
|
|
@@ -3194,7 +3249,11 @@ var KICK_CONTEXTS = {
|
|
|
3194
3249
|
"subscriber-latest": { name: "kick_last_subscriber", username: "kick_last_subscriber" },
|
|
3195
3250
|
"subscriber-new-latest": { name: "kick_last_subscriber", username: "kick_last_subscriber" },
|
|
3196
3251
|
"subscriber-resub-latest": { name: "kick_last_subscriber", username: "kick_last_subscriber" },
|
|
3197
|
-
|
|
3252
|
+
// Mirror the Twitch split: `{sender}` = giver, `{name}` = recipient.
|
|
3253
|
+
// `kick_last_gifted` was added alongside this mapping; see twitch context
|
|
3254
|
+
// above for the rationale (without the split, SE's "{sender} gifted to
|
|
3255
|
+
// {name}" renders the gifter on both sides).
|
|
3256
|
+
"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" },
|
|
3198
3257
|
"cheer-latest": { name: "kick_last_kicks", username: "kick_last_kicks", amount: "kick_last_kicks_amount", count: "kick_last_kicks_amount" },
|
|
3199
3258
|
"raid-latest": { name: "kick_last_host", username: "kick_last_host", amount: "kick_last_host_amount", count: "kick_last_host_amount" },
|
|
3200
3259
|
"follower-session": { amount: "kick_session_follower_count", count: "kick_session_follower_count" },
|
|
@@ -3226,9 +3285,17 @@ var YOUTUBE_CONTEXTS = {
|
|
|
3226
3285
|
"subscriber-week": { amount: "youtube_week_member_count", count: "youtube_week_member_count" },
|
|
3227
3286
|
"subscriber-month": { amount: "youtube_month_member_count", count: "youtube_month_member_count" }
|
|
3228
3287
|
};
|
|
3288
|
+
var RECENT_TO_LATEST_LISTENER = {
|
|
3289
|
+
"follower-recent": "follower-latest",
|
|
3290
|
+
"subscriber-recent": "subscriber-latest",
|
|
3291
|
+
"cheer-recent": "cheer-latest",
|
|
3292
|
+
"tip-recent": "tip-latest",
|
|
3293
|
+
"raid-recent": "raid-latest"
|
|
3294
|
+
};
|
|
3229
3295
|
function listenerPlaceholderContext(listener, provider = "twitch") {
|
|
3296
|
+
const resolved = RECENT_TO_LATEST_LISTENER[listener] ?? listener;
|
|
3230
3297
|
const platformTable = provider === "kick" ? KICK_CONTEXTS : provider === "youtube" ? YOUTUBE_CONTEXTS : null;
|
|
3231
|
-
return platformTable?.[
|
|
3298
|
+
return platformTable?.[resolved] ?? TWITCH_CONTEXTS[resolved] ?? {};
|
|
3232
3299
|
}
|
|
3233
3300
|
var SE_WIDGET_TYPE_LABELS = {
|
|
3234
3301
|
"subscriber-latest": "Latest subscriber",
|
|
@@ -3264,12 +3331,13 @@ function defaultLabelForSEWidget(widget) {
|
|
|
3264
3331
|
const tail = words.slice(1).join(" ");
|
|
3265
3332
|
return tail ? `${head} ${tail}` : head;
|
|
3266
3333
|
}
|
|
3267
|
-
function translateSeText(input, listener, passthrough, provider) {
|
|
3334
|
+
function translateSeText(input, listener, passthrough, provider, options) {
|
|
3268
3335
|
if (!input) return "";
|
|
3269
3336
|
const ctx = listener ? listenerPlaceholderContext(listener, provider) : {};
|
|
3337
|
+
const useAlertFallback = (options?.context ?? "alert") === "alert";
|
|
3270
3338
|
return input.replace(/\{([a-zA-Z][a-zA-Z0-9_.]*)\}/g, (_match, raw) => {
|
|
3271
3339
|
if (passthrough?.has(raw)) return `{{${raw}}}`;
|
|
3272
|
-
const mapped = ctx[raw] ?? SE_TO_LUMIA_PLACEHOLDER[raw] ?? raw;
|
|
3340
|
+
const mapped = ctx[raw] ?? (useAlertFallback ? SE_TO_LUMIA_PLACEHOLDER[raw] : void 0) ?? raw;
|
|
3273
3341
|
return `{{${mapped}}}`;
|
|
3274
3342
|
});
|
|
3275
3343
|
}
|
|
@@ -3313,14 +3381,15 @@ var YOUTUBE_LISTENER_VARS = {
|
|
|
3313
3381
|
};
|
|
3314
3382
|
function listenerToLumiaVariable(listener, provider = "twitch") {
|
|
3315
3383
|
if (typeof listener !== "string" || !listener) return "username";
|
|
3316
|
-
|
|
3317
|
-
if (provider === "
|
|
3384
|
+
const resolvedListener = RECENT_TO_LATEST_LISTENER[listener] ?? listener;
|
|
3385
|
+
if (provider === "kick" && KICK_LISTENER_VARS[resolvedListener]) return KICK_LISTENER_VARS[resolvedListener];
|
|
3386
|
+
if (provider === "youtube" && YOUTUBE_LISTENER_VARS[resolvedListener]) return YOUTUBE_LISTENER_VARS[resolvedListener];
|
|
3318
3387
|
const m = {
|
|
3319
3388
|
// Latest events
|
|
3320
3389
|
"follower-latest": "twitch_last_follower",
|
|
3321
3390
|
"subscriber-latest": "twitch_last_subscriber",
|
|
3322
|
-
"subscriber-new-latest": "
|
|
3323
|
-
"subscriber-resub-latest": "
|
|
3391
|
+
"subscriber-new-latest": "twitch_last_new_subscriber",
|
|
3392
|
+
"subscriber-resub-latest": "twitch_last_resubscriber",
|
|
3324
3393
|
"subscriber-gifted-latest": "twitch_last_gifter",
|
|
3325
3394
|
"subscriber-alltime-gifter": "twitch_alltime_top_gifter",
|
|
3326
3395
|
"tip-latest": "latest_donator",
|
|
@@ -3371,7 +3440,7 @@ function listenerToLumiaVariable(listener, provider = "twitch") {
|
|
|
3371
3440
|
"cheer-alltime-top-donation": "twitch_alltime_top_cheerer_amount",
|
|
3372
3441
|
"cheer-alltime-top-donator": "twitch_alltime_top_cheerer"
|
|
3373
3442
|
};
|
|
3374
|
-
return m[
|
|
3443
|
+
return m[resolvedListener] ?? resolvedListener.replace(/-/g, "_");
|
|
3375
3444
|
}
|
|
3376
3445
|
function seCssToBounds(css, defaults = { width: 600, height: 200 }, canvas) {
|
|
3377
3446
|
let widthIsAuto = false;
|
|
@@ -3475,9 +3544,16 @@ var LUMIA_DEFAULT_SIZES = {
|
|
|
3475
3544
|
// just the card if they want SE's exact footprint.
|
|
3476
3545
|
songrequest: { width: 480, height: 380 }
|
|
3477
3546
|
};
|
|
3478
|
-
function translateVariationCondition(seType, seCondition, requirement) {
|
|
3547
|
+
function translateVariationCondition(seType, seCondition, requirement, seEventType) {
|
|
3479
3548
|
if (seType === "gift") return { conditionType: "IS_GIFT", condition: 1 };
|
|
3480
3549
|
if (seType === "communityGift") return { conditionType: "GIFT_SUB_GREATER", condition: requirement ?? 0 };
|
|
3550
|
+
const isSubscriberEvent = seEventType === "subscriber";
|
|
3551
|
+
if (isSubscriberEvent && seCondition === "ATLEAST" && requirement != null) {
|
|
3552
|
+
return { conditionType: "SUBSCRIBED_MONTHS_GREATER", condition: requirement };
|
|
3553
|
+
}
|
|
3554
|
+
if (isSubscriberEvent && seCondition === "EXACT" && requirement != null) {
|
|
3555
|
+
return { conditionType: "SUBSCRIBED_MONTHS_EQUAL", condition: requirement };
|
|
3556
|
+
}
|
|
3481
3557
|
if (seCondition === "EXACT" && requirement != null) return { conditionType: "EQUAL_NUMBER", condition: requirement };
|
|
3482
3558
|
if (seCondition === "ATLEAST" && requirement != null) {
|
|
3483
3559
|
return { conditionType: "GREATER_NUMBER", condition: requirement };
|
|
@@ -3577,7 +3653,7 @@ function seExtraTextCss(seCss, scrolling) {
|
|
|
3577
3653
|
return extra;
|
|
3578
3654
|
}
|
|
3579
3655
|
function mapText(widget, ctx) {
|
|
3580
|
-
const value = translateSeText(widget.text?.value ?? "");
|
|
3656
|
+
const value = translateSeText(widget.text?.value ?? "", null, void 0, ctx?.provider, { context: "static" });
|
|
3581
3657
|
const seCss = widget.text?.css ?? {};
|
|
3582
3658
|
return buildUnit(widget, "text", {
|
|
3583
3659
|
content: {
|
|
@@ -3634,7 +3710,7 @@ function mapReadout(widget, fallbackVar, ctx) {
|
|
|
3634
3710
|
const variable = listenerToLumiaVariable(listener, ctx?.provider);
|
|
3635
3711
|
const userTemplate = widget.text?.value ?? "";
|
|
3636
3712
|
const inheritsText = userTemplate.trim().length > 0 && /\{[a-zA-Z]/.test(userTemplate);
|
|
3637
|
-
const value = inheritsText ? translateSeText(userTemplate, listener, void 0, ctx?.provider) : `{{${variable}}}`;
|
|
3713
|
+
const value = inheritsText ? translateSeText(userTemplate, listener, void 0, ctx?.provider, { context: "static" }) : `{{${variable}}}`;
|
|
3638
3714
|
const seCss = widget.text?.css ?? {};
|
|
3639
3715
|
return buildUnit(widget, "text", {
|
|
3640
3716
|
content: { value, highlightColor: "inherit" },
|
|
@@ -3889,7 +3965,7 @@ function buildAlertEvent(seEvent, seEventType) {
|
|
|
3889
3965
|
};
|
|
3890
3966
|
}
|
|
3891
3967
|
function buildAlertVariation(v, seEventType, parentSettings) {
|
|
3892
|
-
const translated = translateVariationCondition(v.type ?? null, v.condition ?? null, v.requirement ?? null);
|
|
3968
|
+
const translated = translateVariationCondition(v.type ?? null, v.condition ?? null, v.requirement ?? null, seEventType);
|
|
3893
3969
|
if (!translated) return null;
|
|
3894
3970
|
const { variations: _parentVariations, ...parentInheritable } = parentSettings ?? {};
|
|
3895
3971
|
const mergedSettings = deepMergeSeSettings(parentSettings ? parentInheritable : void 0, v.settings);
|
|
@@ -4025,7 +4101,8 @@ function mapGoal(widget, ctx) {
|
|
|
4025
4101
|
// live `current` floating at the percentage position. See
|
|
4026
4102
|
// components/Modules/Views/Goal/types.ts:GoalTypes.SLIM.
|
|
4027
4103
|
type: "slim",
|
|
4028
|
-
label
|
|
4104
|
+
// Static label rendered above the goal bar — not an alert context.
|
|
4105
|
+
label: translateSeText(v.title ?? "Goal", null, void 0, void 0, { context: "static" }),
|
|
4029
4106
|
subLabel: "",
|
|
4030
4107
|
// The Goal module renders this template literal with {{current}} + {{goal}} substituted.
|
|
4031
4108
|
// Charity goals track donation amounts — prefix `$` so the rendered bar reads
|
|
@@ -4342,6 +4419,9 @@ function mapEventList(widget, ctx) {
|
|
|
4342
4419
|
const filters = computeListenerFilters(widget.listeners, widget.listener);
|
|
4343
4420
|
const maxItems = v.visibleEvents ?? v.visibleItems ?? 5;
|
|
4344
4421
|
return buildUnit(widget, "eventlist", {
|
|
4422
|
+
// SE event-list widgets style per-event lines via widget.text.css; the
|
|
4423
|
+
// Lumia eventlist reads font/size/color from module.css.
|
|
4424
|
+
css: mapSeTextCssToModuleCss(widget, { fontSize: 18 }),
|
|
4345
4425
|
content: {
|
|
4346
4426
|
theme: v.theme ?? "simple",
|
|
4347
4427
|
direction: v.direction ?? "top",
|
|
@@ -4625,7 +4705,7 @@ function mapTimer(widget, ctx) {
|
|
|
4625
4705
|
const v = widget.variables ?? {};
|
|
4626
4706
|
const textValue = typeof widget.text?.value === "string" ? widget.text.value : void 0;
|
|
4627
4707
|
const TIMER_PASSTHROUGH = /* @__PURE__ */ new Set(["days", "hours", "minutes", "seconds"]);
|
|
4628
|
-
const mapTemplate = (template) => translateSeText(template, null, TIMER_PASSTHROUGH);
|
|
4708
|
+
const mapTemplate = (template) => translateSeText(template, null, TIMER_PASSTHROUGH, void 0, { context: "static" });
|
|
4629
4709
|
const absoluteDate = v.date ?? v.endsAt ?? v.endDate;
|
|
4630
4710
|
const target = absoluteDate ? Date.parse(absoluteDate) : NaN;
|
|
4631
4711
|
const hasValidAbsoluteDate = Number.isFinite(target);
|
|
@@ -4697,7 +4777,7 @@ function mapTimer(widget, ctx) {
|
|
|
4697
4777
|
endAnimation: "",
|
|
4698
4778
|
hideAfter: 0,
|
|
4699
4779
|
highlightColor: "inherit",
|
|
4700
|
-
messageAfterCountdown: translateSeText(v.messageAfterCountdown ?? ""),
|
|
4780
|
+
messageAfterCountdown: translateSeText(v.messageAfterCountdown ?? "", null, void 0, void 0, { context: "static" }),
|
|
4701
4781
|
changeMessageAfter: !!v.changeMessageAfterCountdown,
|
|
4702
4782
|
countDate: hasValidAbsoluteDate && (!!v.exactTime || !!v.datePicker || !!absoluteDate),
|
|
4703
4783
|
date: hasValidAbsoluteDate ? new Date(target).toISOString() : "",
|
|
@@ -4775,14 +4855,10 @@ function mapMediaShare(widget, ctx) {
|
|
|
4775
4855
|
// `durationSeconds + grace`).
|
|
4776
4856
|
importMeta: buildImportMeta(widget, { timeLeft: v.timeLeft === true })
|
|
4777
4857
|
},
|
|
4778
|
-
|
|
4779
|
-
|
|
4780
|
-
|
|
4781
|
-
|
|
4782
|
-
textShadow: "rgb(0, 0, 0) 1px 1px 1px",
|
|
4783
|
-
background: "transparent",
|
|
4784
|
-
fontSize: 18
|
|
4785
|
-
}
|
|
4858
|
+
// Lumia songrequest renders the queue list as styled text; pull SE's
|
|
4859
|
+
// widget.text.css so font/size/color carry across (defaults match the
|
|
4860
|
+
// previous hardcoded fallbacks for this widget specifically).
|
|
4861
|
+
css: mapSeTextCssToModuleCss(widget, { fontSize: 18 })
|
|
4786
4862
|
}, ctx);
|
|
4787
4863
|
}
|
|
4788
4864
|
function mapKappagen(widget, ctx) {
|
|
@@ -4844,25 +4920,47 @@ function mapKappagen(widget, ctx) {
|
|
|
4844
4920
|
ctx
|
|
4845
4921
|
);
|
|
4846
4922
|
}
|
|
4847
|
-
|
|
4923
|
+
var TOP_LIST_SORT_BY = {
|
|
4924
|
+
tips: "higherDonations",
|
|
4925
|
+
cheers: "higherBits",
|
|
4926
|
+
gifts: "higherGifts"
|
|
4927
|
+
};
|
|
4928
|
+
var TOP_LIST_TITLE = {
|
|
4929
|
+
tips: "Top Tippers",
|
|
4930
|
+
cheers: "Top Cheerers",
|
|
4931
|
+
gifts: "Top Gifters"
|
|
4932
|
+
};
|
|
4933
|
+
function mapViewerProfilesLeaderboard(widget, currency, ctx) {
|
|
4848
4934
|
const v = widget.variables ?? {};
|
|
4849
4935
|
const count = v.limit ?? v.visibleItems ?? 10;
|
|
4850
|
-
|
|
4851
|
-
|
|
4852
|
-
|
|
4936
|
+
return buildUnit(widget, "viewerprofiles", {
|
|
4937
|
+
// Carry SE text styling so the leaderboard keeps the streamer's
|
|
4938
|
+
// chosen font / size / color.
|
|
4939
|
+
css: mapSeTextCssToModuleCss(widget, { fontSize: 18 }),
|
|
4853
4940
|
content: {
|
|
4854
|
-
|
|
4855
|
-
currency,
|
|
4856
|
-
window,
|
|
4941
|
+
sortBy: TOP_LIST_SORT_BY[currency],
|
|
4857
4942
|
count,
|
|
4858
|
-
title,
|
|
4943
|
+
title: TOP_LIST_TITLE[currency],
|
|
4859
4944
|
showTitle: true,
|
|
4860
|
-
|
|
4861
|
-
|
|
4862
|
-
|
|
4945
|
+
showAvatar: true,
|
|
4946
|
+
showPlatform: true,
|
|
4947
|
+
// Hide noisy columns that aren't relevant for a single-metric
|
|
4948
|
+
// top-X list — the streamer picked "top by gifts/tips/cheers"
|
|
4949
|
+
// for a reason. They can re-enable in Settings if they want.
|
|
4950
|
+
showCommands: false,
|
|
4951
|
+
showAlerts: false,
|
|
4952
|
+
showBits: currency === "cheers",
|
|
4953
|
+
showLastSeen: false,
|
|
4954
|
+
showTopActivity: false,
|
|
4955
|
+
showAchievements: false,
|
|
4956
|
+
showAchievementProgress: false,
|
|
4957
|
+
refreshIntervalSeconds: 30,
|
|
4863
4958
|
itemGap: 8,
|
|
4959
|
+
autoScroll: false,
|
|
4864
4960
|
// Source-side knobs that don't map cleanly survive under the
|
|
4865
|
-
// generic importMeta envelope so the user can re-introspect.
|
|
4961
|
+
// generic importMeta envelope so the user can re-introspect later.
|
|
4962
|
+
// `period` is interesting because viewerprofiles doesn't yet have
|
|
4963
|
+
// a windowed mode — if/when it does, we can promote this.
|
|
4866
4964
|
importMeta: buildImportMeta(widget, {
|
|
4867
4965
|
period: v.period,
|
|
4868
4966
|
offset: v.offset,
|
|
@@ -4876,6 +4974,8 @@ function mapGiveaway(widget, ctx) {
|
|
|
4876
4974
|
const primary = v.colors?.primary ?? "#393853";
|
|
4877
4975
|
const accent = v.colors?.accent ?? "#FF4076";
|
|
4878
4976
|
return buildUnit(widget, "raffle", {
|
|
4977
|
+
// Raffle renders entrants/winner text via module.css.
|
|
4978
|
+
css: mapSeTextCssToModuleCss(widget),
|
|
4879
4979
|
content: {
|
|
4880
4980
|
version: 1,
|
|
4881
4981
|
type: "basic",
|
|
@@ -4960,6 +5060,9 @@ function isHypetrainCustomWidget(widget) {
|
|
|
4960
5060
|
function mapHypetrain(widget, ctx) {
|
|
4961
5061
|
const v = widget.variables ?? {};
|
|
4962
5062
|
return buildUnit(widget, "hypetrain", {
|
|
5063
|
+
// Hypetrain renders level / progress / contributor text — carry SE
|
|
5064
|
+
// styling so an imported hype-train widget keeps its font + accent.
|
|
5065
|
+
css: mapSeTextCssToModuleCss(widget),
|
|
4963
5066
|
content: {
|
|
4964
5067
|
// Twitch-native is the right default — SE hype-train widgets always
|
|
4965
5068
|
// reflected the Twitch native train. Users can flip to 'synthetic'
|
|
@@ -5003,6 +5106,47 @@ function mapHypetrain(widget, ctx) {
|
|
|
5003
5106
|
}
|
|
5004
5107
|
}, ctx);
|
|
5005
5108
|
}
|
|
5109
|
+
function mapEmoteWall(widget, ctx) {
|
|
5110
|
+
const v = widget.variables ?? {};
|
|
5111
|
+
const imageSize = typeof v.emoteSize === "number" ? v.emoteSize : typeof v.size === "number" ? v.size : 40;
|
|
5112
|
+
const maxItemsToShow = typeof v.maxEmotes === "number" && v.maxEmotes > 0 ? v.maxEmotes : typeof v.maxItems === "number" && v.maxItems > 0 ? v.maxItems : 40;
|
|
5113
|
+
const duration = typeof v.duration === "number" && v.duration > 0 ? v.duration : typeof v.decay === "number" && v.decay > 0 ? v.decay : typeof v.decayTime === "number" && v.decayTime > 0 ? v.decayTime : 5;
|
|
5114
|
+
const sites = [];
|
|
5115
|
+
if (v.enableTwitch !== false && v.twitch !== false) sites.push("twitch");
|
|
5116
|
+
if (v.enableBttv !== false && v.bttv !== false) sites.push("bttv");
|
|
5117
|
+
if (v.enableFfz !== false && v.ffz !== false) sites.push("ffz");
|
|
5118
|
+
if (v.enable7tv !== false && v.sevenTv !== false) sites.push("seventv");
|
|
5119
|
+
const animationType = v.direction === "up" ? "rise" : v.direction === "down" ? "fall" : v.direction === "left" || v.direction === "right" ? "slide" : "random";
|
|
5120
|
+
return buildUnit(widget, "emotebox", {
|
|
5121
|
+
content: {
|
|
5122
|
+
version: 1,
|
|
5123
|
+
type: animationType,
|
|
5124
|
+
imageSize,
|
|
5125
|
+
animationTime: duration,
|
|
5126
|
+
duration,
|
|
5127
|
+
maxItemsToShow,
|
|
5128
|
+
delay: typeof v.delay === "number" ? v.delay : 0,
|
|
5129
|
+
sites,
|
|
5130
|
+
background: "transparent",
|
|
5131
|
+
theme: "dark",
|
|
5132
|
+
userLevels: [1, 2, 3, 4, 5, 6, 7, 8, 9],
|
|
5133
|
+
ignoredList: [],
|
|
5134
|
+
excludedAnimations: [],
|
|
5135
|
+
// Emote-wall is chat-driven, not alert-driven — leave alert toggles
|
|
5136
|
+
// off so the widget doesn't double-fire on subscribe/follower events.
|
|
5137
|
+
alertOn: false,
|
|
5138
|
+
alertTriggers: [],
|
|
5139
|
+
alertEmotes: [],
|
|
5140
|
+
alertNumberEmotes: 30,
|
|
5141
|
+
useAnimatedEmotes: true,
|
|
5142
|
+
importMeta: buildImportMeta(widget, {
|
|
5143
|
+
fullscreen: !!v.fullscreen,
|
|
5144
|
+
direction: v.direction
|
|
5145
|
+
})
|
|
5146
|
+
},
|
|
5147
|
+
css: { background: "transparent" }
|
|
5148
|
+
}, ctx);
|
|
5149
|
+
}
|
|
5006
5150
|
function mapUnsupportedAsText(widget, ctx) {
|
|
5007
5151
|
return buildUnit(widget, "text", {
|
|
5008
5152
|
content: {
|
|
@@ -5352,6 +5496,23 @@ var GOAL_TYPES = /* @__PURE__ */ new Set([
|
|
|
5352
5496
|
"se-widget-merch-goal"
|
|
5353
5497
|
]);
|
|
5354
5498
|
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"]);
|
|
5499
|
+
var SCROLLING_RECENT_TYPES = /* @__PURE__ */ new Set([
|
|
5500
|
+
"se-widget-follower-recent",
|
|
5501
|
+
"se-widget-subscriber-recent",
|
|
5502
|
+
"se-widget-cheer-recent",
|
|
5503
|
+
"se-widget-tip-recent"
|
|
5504
|
+
]);
|
|
5505
|
+
var RECENT_TO_LATEST_FALLBACK_VAR = {
|
|
5506
|
+
"se-widget-follower-recent": "twitch_last_follower",
|
|
5507
|
+
"se-widget-subscriber-recent": "twitch_last_subscriber",
|
|
5508
|
+
"se-widget-cheer-recent": "twitch_last_bit",
|
|
5509
|
+
"se-widget-tip-recent": "latest_donator"
|
|
5510
|
+
};
|
|
5511
|
+
function isScrollingRecentWidget(widget) {
|
|
5512
|
+
if (!SCROLLING_RECENT_TYPES.has(widget.type)) return false;
|
|
5513
|
+
const scrolling = widget.text?.scrolling;
|
|
5514
|
+
return !!(scrolling && typeof scrolling === "object" && scrolling.enabled === true);
|
|
5515
|
+
}
|
|
5355
5516
|
function dispatch(widget, ctx) {
|
|
5356
5517
|
const t = widget.type;
|
|
5357
5518
|
console.log("[SE-IMPORT] dispatch widget", {
|
|
@@ -5380,16 +5541,21 @@ function dispatch(widget, ctx) {
|
|
|
5380
5541
|
if (t === "se-widget-donor-drive-alert") return { unit: mapSingleAlert(widget, "donordrive-donation", ctx), status: "direct", lumiaType: "alert" };
|
|
5381
5542
|
if (t === "se-widget-extralife-alert") return { unit: mapSingleAlert(widget, "extralife-donation", ctx), status: "direct", lumiaType: "alert" };
|
|
5382
5543
|
if (t === "se-widget-tiltify-alert") return { unit: mapSingleAlert(widget, "tiltify-campaignDonation", ctx), status: "direct", lumiaType: "alert" };
|
|
5544
|
+
if (isScrollingRecentWidget(widget)) {
|
|
5545
|
+
const fallbackVar = RECENT_TO_LATEST_FALLBACK_VAR[t] ?? "username";
|
|
5546
|
+
return { unit: mapReadout(widget, fallbackVar, ctx), status: "direct", lumiaType: "text" };
|
|
5547
|
+
}
|
|
5383
5548
|
if (EVENTLIST_TYPES.has(t)) return { unit: mapEventList(widget, ctx), status: "direct", lumiaType: "eventlist" };
|
|
5384
|
-
if (t === "se-widget-top-tippers-list") return { unit:
|
|
5385
|
-
if (t === "se-widget-top-cheerers-list") return { unit:
|
|
5386
|
-
if (t === "se-widget-top-gifters-list") return { unit:
|
|
5549
|
+
if (t === "se-widget-top-tippers-list") return { unit: mapViewerProfilesLeaderboard(widget, "tips", ctx), status: "direct", lumiaType: "viewerprofiles" };
|
|
5550
|
+
if (t === "se-widget-top-cheerers-list") return { unit: mapViewerProfilesLeaderboard(widget, "cheers", ctx), status: "direct", lumiaType: "viewerprofiles" };
|
|
5551
|
+
if (t === "se-widget-top-gifters-list") return { unit: mapViewerProfilesLeaderboard(widget, "gifts", ctx), status: "direct", lumiaType: "viewerprofiles" };
|
|
5387
5552
|
if (t === "se-widget-credit-roll") return { unit: mapCredits(widget, ctx), status: "partial", lumiaType: "credits" };
|
|
5388
5553
|
if (t === "se-widget-twitch-chat") return { unit: mapChatbox(widget, ctx), status: "partial", lumiaType: "chatbox" };
|
|
5389
5554
|
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" };
|
|
5390
5555
|
if (t === "se-widget-countdown") return { unit: mapTimer(widget, ctx), status: "partial", lumiaType: "timer" };
|
|
5391
5556
|
if (t === "se-widget-current-song") return { unit: mapNowPlaying(widget, ctx), status: "partial", lumiaType: "nowplaying" };
|
|
5392
5557
|
if (t === "se-widget-kappagen") return { unit: mapKappagen(widget, ctx), status: "partial", lumiaType: "emotealert" };
|
|
5558
|
+
if (t === "se-widget-emote-wall") return { unit: mapEmoteWall(widget, ctx), status: "partial", lumiaType: "emotebox" };
|
|
5393
5559
|
if (t === "se-widget-giveaway") return { unit: mapGiveaway(widget, ctx), status: "direct", lumiaType: "raffle" };
|
|
5394
5560
|
if (t === "se-widget-media-share") return { unit: mapMediaShare(widget, ctx), status: "partial", lumiaType: "songrequest" };
|
|
5395
5561
|
if (t === "se-widget-hype-cup") return { unit: mapHypeCup(widget, ctx), status: "partial", lumiaType: "tipjar" };
|
package/dist/se-import.js
CHANGED
|
@@ -18,31 +18,86 @@ var SE_TO_LUMIA_PLACEHOLDER = {
|
|
|
18
18
|
};
|
|
19
19
|
var TWITCH_CONTEXTS = {
|
|
20
20
|
"follower-latest": { name: "twitch_last_follower", username: "twitch_last_follower" },
|
|
21
|
+
// Three SE listeners with genuinely distinct semantics — preserve the
|
|
22
|
+
// distinction in the variable mapping so a widget configured for "Latest
|
|
23
|
+
// new subscriber" doesn't render the latest *resub* (and vice versa).
|
|
24
|
+
// `subscriber-latest` is the umbrella (anyone subscribing); the other two
|
|
25
|
+
// are subtype-specific. The dedicated TWITCH_LAST_{NEW_SUBSCRIBER,
|
|
26
|
+
// RESUBSCRIBER} variables are written by the Twitch event pipeline on
|
|
27
|
+
// the matching sub.type event.
|
|
21
28
|
"subscriber-latest": { name: "twitch_last_subscriber", username: "twitch_last_subscriber" },
|
|
22
|
-
"subscriber-new-latest": { name: "
|
|
23
|
-
"subscriber-resub-latest": { name: "
|
|
24
|
-
|
|
29
|
+
"subscriber-new-latest": { name: "twitch_last_new_subscriber", username: "twitch_last_new_subscriber" },
|
|
30
|
+
"subscriber-resub-latest": { name: "twitch_last_resubscriber", username: "twitch_last_resubscriber" },
|
|
31
|
+
// `subscriber-gifted-latest`: SE uses `{sender}` for the giver and `{name}` for
|
|
32
|
+
// the recipient (e.g. "{sender} gifted to {name}"). The two roles must point
|
|
33
|
+
// at distinct Lumia variables — collapsing both onto `twitch_last_gifter`
|
|
34
|
+
// rendered "Mr.Beast gifted to Mr.Beast" while the actual recipient was
|
|
35
|
+
// nowhere. `sender` keeps the gifter; `name` / `username` switch to the
|
|
36
|
+
// dedicated recipient variable `twitch_last_gifted`. Amount stays on the
|
|
37
|
+
// gifter (it's the count of subs in that drop, owned by the gifter).
|
|
38
|
+
"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" },
|
|
25
39
|
"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
40
|
"cheer-latest": { name: "twitch_last_bit", username: "twitch_last_bit", amount: "twitch_last_bit_amount", count: "twitch_last_bit_amount" },
|
|
27
41
|
"tip-latest": { name: "latest_donator", username: "latest_donator", amount: "latest_donator_amount", count: "latest_donator_amount", currency: "latest_donator_currency_symbol" },
|
|
28
42
|
latest_donator: { name: "latest_donator", username: "latest_donator", amount: "latest_donator_amount", count: "latest_donator_amount", currency: "latest_donator_currency_symbol" },
|
|
29
43
|
"raid-latest": { name: "twitch_last_raider", username: "twitch_last_raider", amount: "twitch_last_raid_amount", count: "twitch_last_raid_amount" },
|
|
30
|
-
//
|
|
44
|
+
// Period counters: count/amount = the running counter for that window.
|
|
45
|
+
// SE lets the streamer pick session / week / month / total on the same widget
|
|
46
|
+
// type; the chosen period lands in `widget.listener` (e.g. `cheer-week`).
|
|
47
|
+
// Each entry below mirrors a row in the Lumia variables table — the previous
|
|
48
|
+
// table only carried session+total and broke any widget configured for
|
|
49
|
+
// week/month because `{amount}` fell through to the unscoped placeholder
|
|
50
|
+
// and rendered blank.
|
|
31
51
|
"follower-session": { amount: "twitch_session_follower_count", count: "twitch_session_follower_count" },
|
|
32
|
-
"
|
|
33
|
-
"
|
|
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.
|
|
52
|
+
"follower-week": { amount: "twitch_week_follower_count", count: "twitch_week_follower_count" },
|
|
53
|
+
"follower-month": { amount: "twitch_month_follower_count", count: "twitch_month_follower_count" },
|
|
36
54
|
"follower-total": { amount: "twitch_total_follower_count", count: "twitch_total_follower_count" },
|
|
55
|
+
"subscriber-session": { amount: "twitch_session_subscribers_count", count: "twitch_session_subscribers_count" },
|
|
56
|
+
"subscriber-week": { amount: "twitch_week_subscriber_count", count: "twitch_week_subscriber_count" },
|
|
57
|
+
"subscriber-month": { amount: "twitch_month_subscriber_count", count: "twitch_month_subscriber_count" },
|
|
37
58
|
"subscriber-total": { amount: "twitch_total_subscriber_count", count: "twitch_total_subscriber_count" },
|
|
59
|
+
"subscriber-new-session": { amount: "twitch_session_new_subscribers_count", count: "twitch_session_new_subscribers_count" },
|
|
60
|
+
"subscriber-resub-session": { amount: "twitch_session_resub_subscribers_count", count: "twitch_session_resub_subscribers_count" },
|
|
61
|
+
"subscriber-gifted-session": { amount: "twitch_session_gifted_subscribers_count", count: "twitch_session_gifted_subscribers_count" },
|
|
62
|
+
"cheer-session": { amount: "twitch_session_bits_count", count: "twitch_session_bits_count" },
|
|
63
|
+
"cheer-week": { amount: "twitch_week_bits_count", count: "twitch_week_bits_count" },
|
|
64
|
+
"cheer-month": { amount: "twitch_month_bits_count", count: "twitch_month_bits_count" },
|
|
38
65
|
"cheer-total": { amount: "twitch_total_bits_count", count: "twitch_total_bits_count" },
|
|
66
|
+
"tip-session": { amount: "session_donation_amount", count: "session_donation_count", currency: "session_donation_amount_currency_symbol" },
|
|
67
|
+
"tip-week": { amount: "week_donation_amount", count: "week_donation_count", currency: "week_donation_amount_currency_symbol" },
|
|
68
|
+
"tip-month": { amount: "month_donation_amount", count: "month_donation_count", currency: "month_donation_amount_currency_symbol" },
|
|
39
69
|
"tip-total": { amount: "total_donation_amount", count: "total_donation_amount", currency: "total_donation_amount_currency_symbol" },
|
|
40
70
|
"tip-count": { amount: "donation_count", count: "donation_count" },
|
|
41
|
-
// Top cheer / cheerer / donator
|
|
42
|
-
|
|
71
|
+
// Top cheer / cheerer / donator readouts — per period. SE's `se-widget-top-*`
|
|
72
|
+
// widgets encode the chosen period (session / weekly / monthly / alltime) in
|
|
73
|
+
// the widget's `listener` field. Without these entries the user's `{amount}`
|
|
74
|
+
// / `{name}` template tokens fall through to the unscoped placeholder map
|
|
75
|
+
// (`{{amount}}` only resolves inside alert variations) and render blank.
|
|
76
|
+
//
|
|
77
|
+
// `*-top-donation` and `*-top-donator` are paired: SE nominally treats them
|
|
78
|
+
// as "top single event" vs "top cumulative contributor", but the data
|
|
79
|
+
// rendered for both lives in the same Lumia variable pair (`*_top_donator`
|
|
80
|
+
// / `*_top_donator_amount`, `twitch_*_top_cheerer` / `..._amount`). SE
|
|
81
|
+
// widgets routinely use `{name}` against the `-donation` listener to mean
|
|
82
|
+
// "the user who made the top donation", so the two rows below carry the
|
|
83
|
+
// same name/username mapping — anything else falls through to the unscoped
|
|
84
|
+
// `{{username}}` and renders blank.
|
|
85
|
+
"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" },
|
|
43
86
|
"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
|
-
"
|
|
87
|
+
"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" },
|
|
88
|
+
"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" },
|
|
89
|
+
"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" },
|
|
90
|
+
"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" },
|
|
91
|
+
"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" },
|
|
92
|
+
"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" },
|
|
93
|
+
"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" },
|
|
45
94
|
"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" },
|
|
95
|
+
"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" },
|
|
96
|
+
"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" },
|
|
97
|
+
"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" },
|
|
98
|
+
"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" },
|
|
99
|
+
"tip-alltime-top-donation": { name: "top_donator", username: "top_donator", amount: "top_donator_amount", count: "top_donator_amount", currency: "top_donator_currency_symbol" },
|
|
100
|
+
"tip-alltime-top-donator": { name: "top_donator", username: "top_donator", amount: "top_donator_amount", count: "top_donator_amount", currency: "top_donator_currency_symbol" },
|
|
46
101
|
// Botcounter — SE uses `{wins}` for the running chat count; the dispatcher's
|
|
47
102
|
// fallbackVar for `se-widget-botcounter` is `twitch_session_chat_count`, so
|
|
48
103
|
// when the listener arg lands as that var name we treat it as the wins source.
|
|
@@ -79,7 +134,11 @@ var KICK_CONTEXTS = {
|
|
|
79
134
|
"subscriber-latest": { name: "kick_last_subscriber", username: "kick_last_subscriber" },
|
|
80
135
|
"subscriber-new-latest": { name: "kick_last_subscriber", username: "kick_last_subscriber" },
|
|
81
136
|
"subscriber-resub-latest": { name: "kick_last_subscriber", username: "kick_last_subscriber" },
|
|
82
|
-
|
|
137
|
+
// Mirror the Twitch split: `{sender}` = giver, `{name}` = recipient.
|
|
138
|
+
// `kick_last_gifted` was added alongside this mapping; see twitch context
|
|
139
|
+
// above for the rationale (without the split, SE's "{sender} gifted to
|
|
140
|
+
// {name}" renders the gifter on both sides).
|
|
141
|
+
"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" },
|
|
83
142
|
"cheer-latest": { name: "kick_last_kicks", username: "kick_last_kicks", amount: "kick_last_kicks_amount", count: "kick_last_kicks_amount" },
|
|
84
143
|
"raid-latest": { name: "kick_last_host", username: "kick_last_host", amount: "kick_last_host_amount", count: "kick_last_host_amount" },
|
|
85
144
|
"follower-session": { amount: "kick_session_follower_count", count: "kick_session_follower_count" },
|
|
@@ -111,9 +170,17 @@ var YOUTUBE_CONTEXTS = {
|
|
|
111
170
|
"subscriber-week": { amount: "youtube_week_member_count", count: "youtube_week_member_count" },
|
|
112
171
|
"subscriber-month": { amount: "youtube_month_member_count", count: "youtube_month_member_count" }
|
|
113
172
|
};
|
|
173
|
+
var RECENT_TO_LATEST_LISTENER = {
|
|
174
|
+
"follower-recent": "follower-latest",
|
|
175
|
+
"subscriber-recent": "subscriber-latest",
|
|
176
|
+
"cheer-recent": "cheer-latest",
|
|
177
|
+
"tip-recent": "tip-latest",
|
|
178
|
+
"raid-recent": "raid-latest"
|
|
179
|
+
};
|
|
114
180
|
function listenerPlaceholderContext(listener, provider = "twitch") {
|
|
181
|
+
const resolved = RECENT_TO_LATEST_LISTENER[listener] ?? listener;
|
|
115
182
|
const platformTable = provider === "kick" ? KICK_CONTEXTS : provider === "youtube" ? YOUTUBE_CONTEXTS : null;
|
|
116
|
-
return platformTable?.[
|
|
183
|
+
return platformTable?.[resolved] ?? TWITCH_CONTEXTS[resolved] ?? {};
|
|
117
184
|
}
|
|
118
185
|
var SE_WIDGET_TYPE_LABELS = {
|
|
119
186
|
"subscriber-latest": "Latest subscriber",
|
|
@@ -149,12 +216,13 @@ function defaultLabelForSEWidget(widget) {
|
|
|
149
216
|
const tail = words.slice(1).join(" ");
|
|
150
217
|
return tail ? `${head} ${tail}` : head;
|
|
151
218
|
}
|
|
152
|
-
function translateSeText(input, listener, passthrough, provider) {
|
|
219
|
+
function translateSeText(input, listener, passthrough, provider, options) {
|
|
153
220
|
if (!input) return "";
|
|
154
221
|
const ctx = listener ? listenerPlaceholderContext(listener, provider) : {};
|
|
222
|
+
const useAlertFallback = (options?.context ?? "alert") === "alert";
|
|
155
223
|
return input.replace(/\{([a-zA-Z][a-zA-Z0-9_.]*)\}/g, (_match, raw) => {
|
|
156
224
|
if (passthrough?.has(raw)) return `{{${raw}}}`;
|
|
157
|
-
const mapped = ctx[raw] ?? SE_TO_LUMIA_PLACEHOLDER[raw] ?? raw;
|
|
225
|
+
const mapped = ctx[raw] ?? (useAlertFallback ? SE_TO_LUMIA_PLACEHOLDER[raw] : void 0) ?? raw;
|
|
158
226
|
return `{{${mapped}}}`;
|
|
159
227
|
});
|
|
160
228
|
}
|
|
@@ -198,14 +266,15 @@ var YOUTUBE_LISTENER_VARS = {
|
|
|
198
266
|
};
|
|
199
267
|
function listenerToLumiaVariable(listener, provider = "twitch") {
|
|
200
268
|
if (typeof listener !== "string" || !listener) return "username";
|
|
201
|
-
|
|
202
|
-
if (provider === "
|
|
269
|
+
const resolvedListener = RECENT_TO_LATEST_LISTENER[listener] ?? listener;
|
|
270
|
+
if (provider === "kick" && KICK_LISTENER_VARS[resolvedListener]) return KICK_LISTENER_VARS[resolvedListener];
|
|
271
|
+
if (provider === "youtube" && YOUTUBE_LISTENER_VARS[resolvedListener]) return YOUTUBE_LISTENER_VARS[resolvedListener];
|
|
203
272
|
const m = {
|
|
204
273
|
// Latest events
|
|
205
274
|
"follower-latest": "twitch_last_follower",
|
|
206
275
|
"subscriber-latest": "twitch_last_subscriber",
|
|
207
|
-
"subscriber-new-latest": "
|
|
208
|
-
"subscriber-resub-latest": "
|
|
276
|
+
"subscriber-new-latest": "twitch_last_new_subscriber",
|
|
277
|
+
"subscriber-resub-latest": "twitch_last_resubscriber",
|
|
209
278
|
"subscriber-gifted-latest": "twitch_last_gifter",
|
|
210
279
|
"subscriber-alltime-gifter": "twitch_alltime_top_gifter",
|
|
211
280
|
"tip-latest": "latest_donator",
|
|
@@ -256,7 +325,7 @@ function listenerToLumiaVariable(listener, provider = "twitch") {
|
|
|
256
325
|
"cheer-alltime-top-donation": "twitch_alltime_top_cheerer_amount",
|
|
257
326
|
"cheer-alltime-top-donator": "twitch_alltime_top_cheerer"
|
|
258
327
|
};
|
|
259
|
-
return m[
|
|
328
|
+
return m[resolvedListener] ?? resolvedListener.replace(/-/g, "_");
|
|
260
329
|
}
|
|
261
330
|
function seCssToBounds(css, defaults = { width: 600, height: 200 }, canvas) {
|
|
262
331
|
let widthIsAuto = false;
|
|
@@ -360,9 +429,16 @@ var LUMIA_DEFAULT_SIZES = {
|
|
|
360
429
|
// just the card if they want SE's exact footprint.
|
|
361
430
|
songrequest: { width: 480, height: 380 }
|
|
362
431
|
};
|
|
363
|
-
function translateVariationCondition(seType, seCondition, requirement) {
|
|
432
|
+
function translateVariationCondition(seType, seCondition, requirement, seEventType) {
|
|
364
433
|
if (seType === "gift") return { conditionType: "IS_GIFT", condition: 1 };
|
|
365
434
|
if (seType === "communityGift") return { conditionType: "GIFT_SUB_GREATER", condition: requirement ?? 0 };
|
|
435
|
+
const isSubscriberEvent = seEventType === "subscriber";
|
|
436
|
+
if (isSubscriberEvent && seCondition === "ATLEAST" && requirement != null) {
|
|
437
|
+
return { conditionType: "SUBSCRIBED_MONTHS_GREATER", condition: requirement };
|
|
438
|
+
}
|
|
439
|
+
if (isSubscriberEvent && seCondition === "EXACT" && requirement != null) {
|
|
440
|
+
return { conditionType: "SUBSCRIBED_MONTHS_EQUAL", condition: requirement };
|
|
441
|
+
}
|
|
366
442
|
if (seCondition === "EXACT" && requirement != null) return { conditionType: "EQUAL_NUMBER", condition: requirement };
|
|
367
443
|
if (seCondition === "ATLEAST" && requirement != null) {
|
|
368
444
|
return { conditionType: "GREATER_NUMBER", condition: requirement };
|
|
@@ -462,7 +538,7 @@ function seExtraTextCss(seCss, scrolling) {
|
|
|
462
538
|
return extra;
|
|
463
539
|
}
|
|
464
540
|
function mapText(widget, ctx) {
|
|
465
|
-
const value = translateSeText(widget.text?.value ?? "");
|
|
541
|
+
const value = translateSeText(widget.text?.value ?? "", null, void 0, ctx?.provider, { context: "static" });
|
|
466
542
|
const seCss = widget.text?.css ?? {};
|
|
467
543
|
return buildUnit(widget, "text", {
|
|
468
544
|
content: {
|
|
@@ -519,7 +595,7 @@ function mapReadout(widget, fallbackVar, ctx) {
|
|
|
519
595
|
const variable = listenerToLumiaVariable(listener, ctx?.provider);
|
|
520
596
|
const userTemplate = widget.text?.value ?? "";
|
|
521
597
|
const inheritsText = userTemplate.trim().length > 0 && /\{[a-zA-Z]/.test(userTemplate);
|
|
522
|
-
const value = inheritsText ? translateSeText(userTemplate, listener, void 0, ctx?.provider) : `{{${variable}}}`;
|
|
598
|
+
const value = inheritsText ? translateSeText(userTemplate, listener, void 0, ctx?.provider, { context: "static" }) : `{{${variable}}}`;
|
|
523
599
|
const seCss = widget.text?.css ?? {};
|
|
524
600
|
return buildUnit(widget, "text", {
|
|
525
601
|
content: { value, highlightColor: "inherit" },
|
|
@@ -774,7 +850,7 @@ function buildAlertEvent(seEvent, seEventType) {
|
|
|
774
850
|
};
|
|
775
851
|
}
|
|
776
852
|
function buildAlertVariation(v, seEventType, parentSettings) {
|
|
777
|
-
const translated = translateVariationCondition(v.type ?? null, v.condition ?? null, v.requirement ?? null);
|
|
853
|
+
const translated = translateVariationCondition(v.type ?? null, v.condition ?? null, v.requirement ?? null, seEventType);
|
|
778
854
|
if (!translated) return null;
|
|
779
855
|
const { variations: _parentVariations, ...parentInheritable } = parentSettings ?? {};
|
|
780
856
|
const mergedSettings = deepMergeSeSettings(parentSettings ? parentInheritable : void 0, v.settings);
|
|
@@ -910,7 +986,8 @@ function mapGoal(widget, ctx) {
|
|
|
910
986
|
// live `current` floating at the percentage position. See
|
|
911
987
|
// components/Modules/Views/Goal/types.ts:GoalTypes.SLIM.
|
|
912
988
|
type: "slim",
|
|
913
|
-
label
|
|
989
|
+
// Static label rendered above the goal bar — not an alert context.
|
|
990
|
+
label: translateSeText(v.title ?? "Goal", null, void 0, void 0, { context: "static" }),
|
|
914
991
|
subLabel: "",
|
|
915
992
|
// The Goal module renders this template literal with {{current}} + {{goal}} substituted.
|
|
916
993
|
// Charity goals track donation amounts — prefix `$` so the rendered bar reads
|
|
@@ -1227,6 +1304,9 @@ function mapEventList(widget, ctx) {
|
|
|
1227
1304
|
const filters = computeListenerFilters(widget.listeners, widget.listener);
|
|
1228
1305
|
const maxItems = v.visibleEvents ?? v.visibleItems ?? 5;
|
|
1229
1306
|
return buildUnit(widget, "eventlist", {
|
|
1307
|
+
// SE event-list widgets style per-event lines via widget.text.css; the
|
|
1308
|
+
// Lumia eventlist reads font/size/color from module.css.
|
|
1309
|
+
css: mapSeTextCssToModuleCss(widget, { fontSize: 18 }),
|
|
1230
1310
|
content: {
|
|
1231
1311
|
theme: v.theme ?? "simple",
|
|
1232
1312
|
direction: v.direction ?? "top",
|
|
@@ -1510,7 +1590,7 @@ function mapTimer(widget, ctx) {
|
|
|
1510
1590
|
const v = widget.variables ?? {};
|
|
1511
1591
|
const textValue = typeof widget.text?.value === "string" ? widget.text.value : void 0;
|
|
1512
1592
|
const TIMER_PASSTHROUGH = /* @__PURE__ */ new Set(["days", "hours", "minutes", "seconds"]);
|
|
1513
|
-
const mapTemplate = (template) => translateSeText(template, null, TIMER_PASSTHROUGH);
|
|
1593
|
+
const mapTemplate = (template) => translateSeText(template, null, TIMER_PASSTHROUGH, void 0, { context: "static" });
|
|
1514
1594
|
const absoluteDate = v.date ?? v.endsAt ?? v.endDate;
|
|
1515
1595
|
const target = absoluteDate ? Date.parse(absoluteDate) : NaN;
|
|
1516
1596
|
const hasValidAbsoluteDate = Number.isFinite(target);
|
|
@@ -1582,7 +1662,7 @@ function mapTimer(widget, ctx) {
|
|
|
1582
1662
|
endAnimation: "",
|
|
1583
1663
|
hideAfter: 0,
|
|
1584
1664
|
highlightColor: "inherit",
|
|
1585
|
-
messageAfterCountdown: translateSeText(v.messageAfterCountdown ?? ""),
|
|
1665
|
+
messageAfterCountdown: translateSeText(v.messageAfterCountdown ?? "", null, void 0, void 0, { context: "static" }),
|
|
1586
1666
|
changeMessageAfter: !!v.changeMessageAfterCountdown,
|
|
1587
1667
|
countDate: hasValidAbsoluteDate && (!!v.exactTime || !!v.datePicker || !!absoluteDate),
|
|
1588
1668
|
date: hasValidAbsoluteDate ? new Date(target).toISOString() : "",
|
|
@@ -1660,14 +1740,10 @@ function mapMediaShare(widget, ctx) {
|
|
|
1660
1740
|
// `durationSeconds + grace`).
|
|
1661
1741
|
importMeta: buildImportMeta(widget, { timeLeft: v.timeLeft === true })
|
|
1662
1742
|
},
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
textShadow: "rgb(0, 0, 0) 1px 1px 1px",
|
|
1668
|
-
background: "transparent",
|
|
1669
|
-
fontSize: 18
|
|
1670
|
-
}
|
|
1743
|
+
// Lumia songrequest renders the queue list as styled text; pull SE's
|
|
1744
|
+
// widget.text.css so font/size/color carry across (defaults match the
|
|
1745
|
+
// previous hardcoded fallbacks for this widget specifically).
|
|
1746
|
+
css: mapSeTextCssToModuleCss(widget, { fontSize: 18 })
|
|
1671
1747
|
}, ctx);
|
|
1672
1748
|
}
|
|
1673
1749
|
function mapKappagen(widget, ctx) {
|
|
@@ -1729,25 +1805,47 @@ function mapKappagen(widget, ctx) {
|
|
|
1729
1805
|
ctx
|
|
1730
1806
|
);
|
|
1731
1807
|
}
|
|
1732
|
-
|
|
1808
|
+
var TOP_LIST_SORT_BY = {
|
|
1809
|
+
tips: "higherDonations",
|
|
1810
|
+
cheers: "higherBits",
|
|
1811
|
+
gifts: "higherGifts"
|
|
1812
|
+
};
|
|
1813
|
+
var TOP_LIST_TITLE = {
|
|
1814
|
+
tips: "Top Tippers",
|
|
1815
|
+
cheers: "Top Cheerers",
|
|
1816
|
+
gifts: "Top Gifters"
|
|
1817
|
+
};
|
|
1818
|
+
function mapViewerProfilesLeaderboard(widget, currency, ctx) {
|
|
1733
1819
|
const v = widget.variables ?? {};
|
|
1734
1820
|
const count = v.limit ?? v.visibleItems ?? 10;
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1821
|
+
return buildUnit(widget, "viewerprofiles", {
|
|
1822
|
+
// Carry SE text styling so the leaderboard keeps the streamer's
|
|
1823
|
+
// chosen font / size / color.
|
|
1824
|
+
css: mapSeTextCssToModuleCss(widget, { fontSize: 18 }),
|
|
1738
1825
|
content: {
|
|
1739
|
-
|
|
1740
|
-
currency,
|
|
1741
|
-
window,
|
|
1826
|
+
sortBy: TOP_LIST_SORT_BY[currency],
|
|
1742
1827
|
count,
|
|
1743
|
-
title,
|
|
1828
|
+
title: TOP_LIST_TITLE[currency],
|
|
1744
1829
|
showTitle: true,
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1830
|
+
showAvatar: true,
|
|
1831
|
+
showPlatform: true,
|
|
1832
|
+
// Hide noisy columns that aren't relevant for a single-metric
|
|
1833
|
+
// top-X list — the streamer picked "top by gifts/tips/cheers"
|
|
1834
|
+
// for a reason. They can re-enable in Settings if they want.
|
|
1835
|
+
showCommands: false,
|
|
1836
|
+
showAlerts: false,
|
|
1837
|
+
showBits: currency === "cheers",
|
|
1838
|
+
showLastSeen: false,
|
|
1839
|
+
showTopActivity: false,
|
|
1840
|
+
showAchievements: false,
|
|
1841
|
+
showAchievementProgress: false,
|
|
1842
|
+
refreshIntervalSeconds: 30,
|
|
1748
1843
|
itemGap: 8,
|
|
1844
|
+
autoScroll: false,
|
|
1749
1845
|
// Source-side knobs that don't map cleanly survive under the
|
|
1750
|
-
// generic importMeta envelope so the user can re-introspect.
|
|
1846
|
+
// generic importMeta envelope so the user can re-introspect later.
|
|
1847
|
+
// `period` is interesting because viewerprofiles doesn't yet have
|
|
1848
|
+
// a windowed mode — if/when it does, we can promote this.
|
|
1751
1849
|
importMeta: buildImportMeta(widget, {
|
|
1752
1850
|
period: v.period,
|
|
1753
1851
|
offset: v.offset,
|
|
@@ -1761,6 +1859,8 @@ function mapGiveaway(widget, ctx) {
|
|
|
1761
1859
|
const primary = v.colors?.primary ?? "#393853";
|
|
1762
1860
|
const accent = v.colors?.accent ?? "#FF4076";
|
|
1763
1861
|
return buildUnit(widget, "raffle", {
|
|
1862
|
+
// Raffle renders entrants/winner text via module.css.
|
|
1863
|
+
css: mapSeTextCssToModuleCss(widget),
|
|
1764
1864
|
content: {
|
|
1765
1865
|
version: 1,
|
|
1766
1866
|
type: "basic",
|
|
@@ -1845,6 +1945,9 @@ function isHypetrainCustomWidget(widget) {
|
|
|
1845
1945
|
function mapHypetrain(widget, ctx) {
|
|
1846
1946
|
const v = widget.variables ?? {};
|
|
1847
1947
|
return buildUnit(widget, "hypetrain", {
|
|
1948
|
+
// Hypetrain renders level / progress / contributor text — carry SE
|
|
1949
|
+
// styling so an imported hype-train widget keeps its font + accent.
|
|
1950
|
+
css: mapSeTextCssToModuleCss(widget),
|
|
1848
1951
|
content: {
|
|
1849
1952
|
// Twitch-native is the right default — SE hype-train widgets always
|
|
1850
1953
|
// reflected the Twitch native train. Users can flip to 'synthetic'
|
|
@@ -1888,6 +1991,47 @@ function mapHypetrain(widget, ctx) {
|
|
|
1888
1991
|
}
|
|
1889
1992
|
}, ctx);
|
|
1890
1993
|
}
|
|
1994
|
+
function mapEmoteWall(widget, ctx) {
|
|
1995
|
+
const v = widget.variables ?? {};
|
|
1996
|
+
const imageSize = typeof v.emoteSize === "number" ? v.emoteSize : typeof v.size === "number" ? v.size : 40;
|
|
1997
|
+
const maxItemsToShow = typeof v.maxEmotes === "number" && v.maxEmotes > 0 ? v.maxEmotes : typeof v.maxItems === "number" && v.maxItems > 0 ? v.maxItems : 40;
|
|
1998
|
+
const duration = typeof v.duration === "number" && v.duration > 0 ? v.duration : typeof v.decay === "number" && v.decay > 0 ? v.decay : typeof v.decayTime === "number" && v.decayTime > 0 ? v.decayTime : 5;
|
|
1999
|
+
const sites = [];
|
|
2000
|
+
if (v.enableTwitch !== false && v.twitch !== false) sites.push("twitch");
|
|
2001
|
+
if (v.enableBttv !== false && v.bttv !== false) sites.push("bttv");
|
|
2002
|
+
if (v.enableFfz !== false && v.ffz !== false) sites.push("ffz");
|
|
2003
|
+
if (v.enable7tv !== false && v.sevenTv !== false) sites.push("seventv");
|
|
2004
|
+
const animationType = v.direction === "up" ? "rise" : v.direction === "down" ? "fall" : v.direction === "left" || v.direction === "right" ? "slide" : "random";
|
|
2005
|
+
return buildUnit(widget, "emotebox", {
|
|
2006
|
+
content: {
|
|
2007
|
+
version: 1,
|
|
2008
|
+
type: animationType,
|
|
2009
|
+
imageSize,
|
|
2010
|
+
animationTime: duration,
|
|
2011
|
+
duration,
|
|
2012
|
+
maxItemsToShow,
|
|
2013
|
+
delay: typeof v.delay === "number" ? v.delay : 0,
|
|
2014
|
+
sites,
|
|
2015
|
+
background: "transparent",
|
|
2016
|
+
theme: "dark",
|
|
2017
|
+
userLevels: [1, 2, 3, 4, 5, 6, 7, 8, 9],
|
|
2018
|
+
ignoredList: [],
|
|
2019
|
+
excludedAnimations: [],
|
|
2020
|
+
// Emote-wall is chat-driven, not alert-driven — leave alert toggles
|
|
2021
|
+
// off so the widget doesn't double-fire on subscribe/follower events.
|
|
2022
|
+
alertOn: false,
|
|
2023
|
+
alertTriggers: [],
|
|
2024
|
+
alertEmotes: [],
|
|
2025
|
+
alertNumberEmotes: 30,
|
|
2026
|
+
useAnimatedEmotes: true,
|
|
2027
|
+
importMeta: buildImportMeta(widget, {
|
|
2028
|
+
fullscreen: !!v.fullscreen,
|
|
2029
|
+
direction: v.direction
|
|
2030
|
+
})
|
|
2031
|
+
},
|
|
2032
|
+
css: { background: "transparent" }
|
|
2033
|
+
}, ctx);
|
|
2034
|
+
}
|
|
1891
2035
|
function mapUnsupportedAsText(widget, ctx) {
|
|
1892
2036
|
return buildUnit(widget, "text", {
|
|
1893
2037
|
content: {
|
|
@@ -2237,6 +2381,23 @@ var GOAL_TYPES = /* @__PURE__ */ new Set([
|
|
|
2237
2381
|
"se-widget-merch-goal"
|
|
2238
2382
|
]);
|
|
2239
2383
|
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"]);
|
|
2384
|
+
var SCROLLING_RECENT_TYPES = /* @__PURE__ */ new Set([
|
|
2385
|
+
"se-widget-follower-recent",
|
|
2386
|
+
"se-widget-subscriber-recent",
|
|
2387
|
+
"se-widget-cheer-recent",
|
|
2388
|
+
"se-widget-tip-recent"
|
|
2389
|
+
]);
|
|
2390
|
+
var RECENT_TO_LATEST_FALLBACK_VAR = {
|
|
2391
|
+
"se-widget-follower-recent": "twitch_last_follower",
|
|
2392
|
+
"se-widget-subscriber-recent": "twitch_last_subscriber",
|
|
2393
|
+
"se-widget-cheer-recent": "twitch_last_bit",
|
|
2394
|
+
"se-widget-tip-recent": "latest_donator"
|
|
2395
|
+
};
|
|
2396
|
+
function isScrollingRecentWidget(widget) {
|
|
2397
|
+
if (!SCROLLING_RECENT_TYPES.has(widget.type)) return false;
|
|
2398
|
+
const scrolling = widget.text?.scrolling;
|
|
2399
|
+
return !!(scrolling && typeof scrolling === "object" && scrolling.enabled === true);
|
|
2400
|
+
}
|
|
2240
2401
|
function dispatch(widget, ctx) {
|
|
2241
2402
|
const t = widget.type;
|
|
2242
2403
|
console.log("[SE-IMPORT] dispatch widget", {
|
|
@@ -2265,16 +2426,21 @@ function dispatch(widget, ctx) {
|
|
|
2265
2426
|
if (t === "se-widget-donor-drive-alert") return { unit: mapSingleAlert(widget, "donordrive-donation", ctx), status: "direct", lumiaType: "alert" };
|
|
2266
2427
|
if (t === "se-widget-extralife-alert") return { unit: mapSingleAlert(widget, "extralife-donation", ctx), status: "direct", lumiaType: "alert" };
|
|
2267
2428
|
if (t === "se-widget-tiltify-alert") return { unit: mapSingleAlert(widget, "tiltify-campaignDonation", ctx), status: "direct", lumiaType: "alert" };
|
|
2429
|
+
if (isScrollingRecentWidget(widget)) {
|
|
2430
|
+
const fallbackVar = RECENT_TO_LATEST_FALLBACK_VAR[t] ?? "username";
|
|
2431
|
+
return { unit: mapReadout(widget, fallbackVar, ctx), status: "direct", lumiaType: "text" };
|
|
2432
|
+
}
|
|
2268
2433
|
if (EVENTLIST_TYPES.has(t)) return { unit: mapEventList(widget, ctx), status: "direct", lumiaType: "eventlist" };
|
|
2269
|
-
if (t === "se-widget-top-tippers-list") return { unit:
|
|
2270
|
-
if (t === "se-widget-top-cheerers-list") return { unit:
|
|
2271
|
-
if (t === "se-widget-top-gifters-list") return { unit:
|
|
2434
|
+
if (t === "se-widget-top-tippers-list") return { unit: mapViewerProfilesLeaderboard(widget, "tips", ctx), status: "direct", lumiaType: "viewerprofiles" };
|
|
2435
|
+
if (t === "se-widget-top-cheerers-list") return { unit: mapViewerProfilesLeaderboard(widget, "cheers", ctx), status: "direct", lumiaType: "viewerprofiles" };
|
|
2436
|
+
if (t === "se-widget-top-gifters-list") return { unit: mapViewerProfilesLeaderboard(widget, "gifts", ctx), status: "direct", lumiaType: "viewerprofiles" };
|
|
2272
2437
|
if (t === "se-widget-credit-roll") return { unit: mapCredits(widget, ctx), status: "partial", lumiaType: "credits" };
|
|
2273
2438
|
if (t === "se-widget-twitch-chat") return { unit: mapChatbox(widget, ctx), status: "partial", lumiaType: "chatbox" };
|
|
2274
2439
|
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" };
|
|
2275
2440
|
if (t === "se-widget-countdown") return { unit: mapTimer(widget, ctx), status: "partial", lumiaType: "timer" };
|
|
2276
2441
|
if (t === "se-widget-current-song") return { unit: mapNowPlaying(widget, ctx), status: "partial", lumiaType: "nowplaying" };
|
|
2277
2442
|
if (t === "se-widget-kappagen") return { unit: mapKappagen(widget, ctx), status: "partial", lumiaType: "emotealert" };
|
|
2443
|
+
if (t === "se-widget-emote-wall") return { unit: mapEmoteWall(widget, ctx), status: "partial", lumiaType: "emotebox" };
|
|
2278
2444
|
if (t === "se-widget-giveaway") return { unit: mapGiveaway(widget, ctx), status: "direct", lumiaType: "raffle" };
|
|
2279
2445
|
if (t === "se-widget-media-share") return { unit: mapMediaShare(widget, ctx), status: "partial", lumiaType: "songrequest" };
|
|
2280
2446
|
if (t === "se-widget-hype-cup") return { unit: mapHypeCup(widget, ctx), status: "partial", lumiaType: "tipjar" };
|