@lumiastream/ui 0.2.8-alpha.27 → 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 +168 -39
- package/dist/se-import.js +168 -39
- 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" },
|
|
@@ -3272,12 +3331,13 @@ function defaultLabelForSEWidget(widget) {
|
|
|
3272
3331
|
const tail = words.slice(1).join(" ");
|
|
3273
3332
|
return tail ? `${head} ${tail}` : head;
|
|
3274
3333
|
}
|
|
3275
|
-
function translateSeText(input, listener, passthrough, provider) {
|
|
3334
|
+
function translateSeText(input, listener, passthrough, provider, options) {
|
|
3276
3335
|
if (!input) return "";
|
|
3277
3336
|
const ctx = listener ? listenerPlaceholderContext(listener, provider) : {};
|
|
3337
|
+
const useAlertFallback = (options?.context ?? "alert") === "alert";
|
|
3278
3338
|
return input.replace(/\{([a-zA-Z][a-zA-Z0-9_.]*)\}/g, (_match, raw) => {
|
|
3279
3339
|
if (passthrough?.has(raw)) return `{{${raw}}}`;
|
|
3280
|
-
const mapped = ctx[raw] ?? SE_TO_LUMIA_PLACEHOLDER[raw] ?? raw;
|
|
3340
|
+
const mapped = ctx[raw] ?? (useAlertFallback ? SE_TO_LUMIA_PLACEHOLDER[raw] : void 0) ?? raw;
|
|
3281
3341
|
return `{{${mapped}}}`;
|
|
3282
3342
|
});
|
|
3283
3343
|
}
|
|
@@ -3328,8 +3388,8 @@ function listenerToLumiaVariable(listener, provider = "twitch") {
|
|
|
3328
3388
|
// Latest events
|
|
3329
3389
|
"follower-latest": "twitch_last_follower",
|
|
3330
3390
|
"subscriber-latest": "twitch_last_subscriber",
|
|
3331
|
-
"subscriber-new-latest": "
|
|
3332
|
-
"subscriber-resub-latest": "
|
|
3391
|
+
"subscriber-new-latest": "twitch_last_new_subscriber",
|
|
3392
|
+
"subscriber-resub-latest": "twitch_last_resubscriber",
|
|
3333
3393
|
"subscriber-gifted-latest": "twitch_last_gifter",
|
|
3334
3394
|
"subscriber-alltime-gifter": "twitch_alltime_top_gifter",
|
|
3335
3395
|
"tip-latest": "latest_donator",
|
|
@@ -3484,9 +3544,16 @@ var LUMIA_DEFAULT_SIZES = {
|
|
|
3484
3544
|
// just the card if they want SE's exact footprint.
|
|
3485
3545
|
songrequest: { width: 480, height: 380 }
|
|
3486
3546
|
};
|
|
3487
|
-
function translateVariationCondition(seType, seCondition, requirement) {
|
|
3547
|
+
function translateVariationCondition(seType, seCondition, requirement, seEventType) {
|
|
3488
3548
|
if (seType === "gift") return { conditionType: "IS_GIFT", condition: 1 };
|
|
3489
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
|
+
}
|
|
3490
3557
|
if (seCondition === "EXACT" && requirement != null) return { conditionType: "EQUAL_NUMBER", condition: requirement };
|
|
3491
3558
|
if (seCondition === "ATLEAST" && requirement != null) {
|
|
3492
3559
|
return { conditionType: "GREATER_NUMBER", condition: requirement };
|
|
@@ -3586,7 +3653,7 @@ function seExtraTextCss(seCss, scrolling) {
|
|
|
3586
3653
|
return extra;
|
|
3587
3654
|
}
|
|
3588
3655
|
function mapText(widget, ctx) {
|
|
3589
|
-
const value = translateSeText(widget.text?.value ?? "");
|
|
3656
|
+
const value = translateSeText(widget.text?.value ?? "", null, void 0, ctx?.provider, { context: "static" });
|
|
3590
3657
|
const seCss = widget.text?.css ?? {};
|
|
3591
3658
|
return buildUnit(widget, "text", {
|
|
3592
3659
|
content: {
|
|
@@ -3643,7 +3710,7 @@ function mapReadout(widget, fallbackVar, ctx) {
|
|
|
3643
3710
|
const variable = listenerToLumiaVariable(listener, ctx?.provider);
|
|
3644
3711
|
const userTemplate = widget.text?.value ?? "";
|
|
3645
3712
|
const inheritsText = userTemplate.trim().length > 0 && /\{[a-zA-Z]/.test(userTemplate);
|
|
3646
|
-
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}}}`;
|
|
3647
3714
|
const seCss = widget.text?.css ?? {};
|
|
3648
3715
|
return buildUnit(widget, "text", {
|
|
3649
3716
|
content: { value, highlightColor: "inherit" },
|
|
@@ -3898,7 +3965,7 @@ function buildAlertEvent(seEvent, seEventType) {
|
|
|
3898
3965
|
};
|
|
3899
3966
|
}
|
|
3900
3967
|
function buildAlertVariation(v, seEventType, parentSettings) {
|
|
3901
|
-
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);
|
|
3902
3969
|
if (!translated) return null;
|
|
3903
3970
|
const { variations: _parentVariations, ...parentInheritable } = parentSettings ?? {};
|
|
3904
3971
|
const mergedSettings = deepMergeSeSettings(parentSettings ? parentInheritable : void 0, v.settings);
|
|
@@ -4034,7 +4101,8 @@ function mapGoal(widget, ctx) {
|
|
|
4034
4101
|
// live `current` floating at the percentage position. See
|
|
4035
4102
|
// components/Modules/Views/Goal/types.ts:GoalTypes.SLIM.
|
|
4036
4103
|
type: "slim",
|
|
4037
|
-
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" }),
|
|
4038
4106
|
subLabel: "",
|
|
4039
4107
|
// The Goal module renders this template literal with {{current}} + {{goal}} substituted.
|
|
4040
4108
|
// Charity goals track donation amounts — prefix `$` so the rendered bar reads
|
|
@@ -4637,7 +4705,7 @@ function mapTimer(widget, ctx) {
|
|
|
4637
4705
|
const v = widget.variables ?? {};
|
|
4638
4706
|
const textValue = typeof widget.text?.value === "string" ? widget.text.value : void 0;
|
|
4639
4707
|
const TIMER_PASSTHROUGH = /* @__PURE__ */ new Set(["days", "hours", "minutes", "seconds"]);
|
|
4640
|
-
const mapTemplate = (template) => translateSeText(template, null, TIMER_PASSTHROUGH);
|
|
4708
|
+
const mapTemplate = (template) => translateSeText(template, null, TIMER_PASSTHROUGH, void 0, { context: "static" });
|
|
4641
4709
|
const absoluteDate = v.date ?? v.endsAt ?? v.endDate;
|
|
4642
4710
|
const target = absoluteDate ? Date.parse(absoluteDate) : NaN;
|
|
4643
4711
|
const hasValidAbsoluteDate = Number.isFinite(target);
|
|
@@ -4709,7 +4777,7 @@ function mapTimer(widget, ctx) {
|
|
|
4709
4777
|
endAnimation: "",
|
|
4710
4778
|
hideAfter: 0,
|
|
4711
4779
|
highlightColor: "inherit",
|
|
4712
|
-
messageAfterCountdown: translateSeText(v.messageAfterCountdown ?? ""),
|
|
4780
|
+
messageAfterCountdown: translateSeText(v.messageAfterCountdown ?? "", null, void 0, void 0, { context: "static" }),
|
|
4713
4781
|
changeMessageAfter: !!v.changeMessageAfterCountdown,
|
|
4714
4782
|
countDate: hasValidAbsoluteDate && (!!v.exactTime || !!v.datePicker || !!absoluteDate),
|
|
4715
4783
|
date: hasValidAbsoluteDate ? new Date(target).toISOString() : "",
|
|
@@ -4852,28 +4920,47 @@ function mapKappagen(widget, ctx) {
|
|
|
4852
4920
|
ctx
|
|
4853
4921
|
);
|
|
4854
4922
|
}
|
|
4855
|
-
|
|
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) {
|
|
4856
4934
|
const v = widget.variables ?? {};
|
|
4857
4935
|
const count = v.limit ?? v.visibleItems ?? 10;
|
|
4858
|
-
|
|
4859
|
-
|
|
4860
|
-
return buildUnit(widget, "loyaltyleaderboard", {
|
|
4861
|
-
// Carry SE text styling so the leaderboard list keeps the streamer's
|
|
4936
|
+
return buildUnit(widget, "viewerprofiles", {
|
|
4937
|
+
// Carry SE text styling so the leaderboard keeps the streamer's
|
|
4862
4938
|
// chosen font / size / color.
|
|
4863
4939
|
css: mapSeTextCssToModuleCss(widget, { fontSize: 18 }),
|
|
4864
4940
|
content: {
|
|
4865
|
-
|
|
4866
|
-
currency,
|
|
4867
|
-
window,
|
|
4941
|
+
sortBy: TOP_LIST_SORT_BY[currency],
|
|
4868
4942
|
count,
|
|
4869
|
-
title,
|
|
4943
|
+
title: TOP_LIST_TITLE[currency],
|
|
4870
4944
|
showTitle: true,
|
|
4871
|
-
|
|
4872
|
-
|
|
4873
|
-
|
|
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,
|
|
4874
4958
|
itemGap: 8,
|
|
4959
|
+
autoScroll: false,
|
|
4875
4960
|
// Source-side knobs that don't map cleanly survive under the
|
|
4876
|
-
// 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.
|
|
4877
4964
|
importMeta: buildImportMeta(widget, {
|
|
4878
4965
|
period: v.period,
|
|
4879
4966
|
offset: v.offset,
|
|
@@ -5019,6 +5106,47 @@ function mapHypetrain(widget, ctx) {
|
|
|
5019
5106
|
}
|
|
5020
5107
|
}, ctx);
|
|
5021
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
|
+
}
|
|
5022
5150
|
function mapUnsupportedAsText(widget, ctx) {
|
|
5023
5151
|
return buildUnit(widget, "text", {
|
|
5024
5152
|
content: {
|
|
@@ -5418,15 +5546,16 @@ function dispatch(widget, ctx) {
|
|
|
5418
5546
|
return { unit: mapReadout(widget, fallbackVar, ctx), status: "direct", lumiaType: "text" };
|
|
5419
5547
|
}
|
|
5420
5548
|
if (EVENTLIST_TYPES.has(t)) return { unit: mapEventList(widget, ctx), status: "direct", lumiaType: "eventlist" };
|
|
5421
|
-
if (t === "se-widget-top-tippers-list") return { unit:
|
|
5422
|
-
if (t === "se-widget-top-cheerers-list") return { unit:
|
|
5423
|
-
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" };
|
|
5424
5552
|
if (t === "se-widget-credit-roll") return { unit: mapCredits(widget, ctx), status: "partial", lumiaType: "credits" };
|
|
5425
5553
|
if (t === "se-widget-twitch-chat") return { unit: mapChatbox(widget, ctx), status: "partial", lumiaType: "chatbox" };
|
|
5426
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" };
|
|
5427
5555
|
if (t === "se-widget-countdown") return { unit: mapTimer(widget, ctx), status: "partial", lumiaType: "timer" };
|
|
5428
5556
|
if (t === "se-widget-current-song") return { unit: mapNowPlaying(widget, ctx), status: "partial", lumiaType: "nowplaying" };
|
|
5429
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" };
|
|
5430
5559
|
if (t === "se-widget-giveaway") return { unit: mapGiveaway(widget, ctx), status: "direct", lumiaType: "raffle" };
|
|
5431
5560
|
if (t === "se-widget-media-share") return { unit: mapMediaShare(widget, ctx), status: "partial", lumiaType: "songrequest" };
|
|
5432
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" },
|
|
@@ -157,12 +216,13 @@ function defaultLabelForSEWidget(widget) {
|
|
|
157
216
|
const tail = words.slice(1).join(" ");
|
|
158
217
|
return tail ? `${head} ${tail}` : head;
|
|
159
218
|
}
|
|
160
|
-
function translateSeText(input, listener, passthrough, provider) {
|
|
219
|
+
function translateSeText(input, listener, passthrough, provider, options) {
|
|
161
220
|
if (!input) return "";
|
|
162
221
|
const ctx = listener ? listenerPlaceholderContext(listener, provider) : {};
|
|
222
|
+
const useAlertFallback = (options?.context ?? "alert") === "alert";
|
|
163
223
|
return input.replace(/\{([a-zA-Z][a-zA-Z0-9_.]*)\}/g, (_match, raw) => {
|
|
164
224
|
if (passthrough?.has(raw)) return `{{${raw}}}`;
|
|
165
|
-
const mapped = ctx[raw] ?? SE_TO_LUMIA_PLACEHOLDER[raw] ?? raw;
|
|
225
|
+
const mapped = ctx[raw] ?? (useAlertFallback ? SE_TO_LUMIA_PLACEHOLDER[raw] : void 0) ?? raw;
|
|
166
226
|
return `{{${mapped}}}`;
|
|
167
227
|
});
|
|
168
228
|
}
|
|
@@ -213,8 +273,8 @@ function listenerToLumiaVariable(listener, provider = "twitch") {
|
|
|
213
273
|
// Latest events
|
|
214
274
|
"follower-latest": "twitch_last_follower",
|
|
215
275
|
"subscriber-latest": "twitch_last_subscriber",
|
|
216
|
-
"subscriber-new-latest": "
|
|
217
|
-
"subscriber-resub-latest": "
|
|
276
|
+
"subscriber-new-latest": "twitch_last_new_subscriber",
|
|
277
|
+
"subscriber-resub-latest": "twitch_last_resubscriber",
|
|
218
278
|
"subscriber-gifted-latest": "twitch_last_gifter",
|
|
219
279
|
"subscriber-alltime-gifter": "twitch_alltime_top_gifter",
|
|
220
280
|
"tip-latest": "latest_donator",
|
|
@@ -369,9 +429,16 @@ var LUMIA_DEFAULT_SIZES = {
|
|
|
369
429
|
// just the card if they want SE's exact footprint.
|
|
370
430
|
songrequest: { width: 480, height: 380 }
|
|
371
431
|
};
|
|
372
|
-
function translateVariationCondition(seType, seCondition, requirement) {
|
|
432
|
+
function translateVariationCondition(seType, seCondition, requirement, seEventType) {
|
|
373
433
|
if (seType === "gift") return { conditionType: "IS_GIFT", condition: 1 };
|
|
374
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
|
+
}
|
|
375
442
|
if (seCondition === "EXACT" && requirement != null) return { conditionType: "EQUAL_NUMBER", condition: requirement };
|
|
376
443
|
if (seCondition === "ATLEAST" && requirement != null) {
|
|
377
444
|
return { conditionType: "GREATER_NUMBER", condition: requirement };
|
|
@@ -471,7 +538,7 @@ function seExtraTextCss(seCss, scrolling) {
|
|
|
471
538
|
return extra;
|
|
472
539
|
}
|
|
473
540
|
function mapText(widget, ctx) {
|
|
474
|
-
const value = translateSeText(widget.text?.value ?? "");
|
|
541
|
+
const value = translateSeText(widget.text?.value ?? "", null, void 0, ctx?.provider, { context: "static" });
|
|
475
542
|
const seCss = widget.text?.css ?? {};
|
|
476
543
|
return buildUnit(widget, "text", {
|
|
477
544
|
content: {
|
|
@@ -528,7 +595,7 @@ function mapReadout(widget, fallbackVar, ctx) {
|
|
|
528
595
|
const variable = listenerToLumiaVariable(listener, ctx?.provider);
|
|
529
596
|
const userTemplate = widget.text?.value ?? "";
|
|
530
597
|
const inheritsText = userTemplate.trim().length > 0 && /\{[a-zA-Z]/.test(userTemplate);
|
|
531
|
-
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}}}`;
|
|
532
599
|
const seCss = widget.text?.css ?? {};
|
|
533
600
|
return buildUnit(widget, "text", {
|
|
534
601
|
content: { value, highlightColor: "inherit" },
|
|
@@ -783,7 +850,7 @@ function buildAlertEvent(seEvent, seEventType) {
|
|
|
783
850
|
};
|
|
784
851
|
}
|
|
785
852
|
function buildAlertVariation(v, seEventType, parentSettings) {
|
|
786
|
-
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);
|
|
787
854
|
if (!translated) return null;
|
|
788
855
|
const { variations: _parentVariations, ...parentInheritable } = parentSettings ?? {};
|
|
789
856
|
const mergedSettings = deepMergeSeSettings(parentSettings ? parentInheritable : void 0, v.settings);
|
|
@@ -919,7 +986,8 @@ function mapGoal(widget, ctx) {
|
|
|
919
986
|
// live `current` floating at the percentage position. See
|
|
920
987
|
// components/Modules/Views/Goal/types.ts:GoalTypes.SLIM.
|
|
921
988
|
type: "slim",
|
|
922
|
-
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" }),
|
|
923
991
|
subLabel: "",
|
|
924
992
|
// The Goal module renders this template literal with {{current}} + {{goal}} substituted.
|
|
925
993
|
// Charity goals track donation amounts — prefix `$` so the rendered bar reads
|
|
@@ -1522,7 +1590,7 @@ function mapTimer(widget, ctx) {
|
|
|
1522
1590
|
const v = widget.variables ?? {};
|
|
1523
1591
|
const textValue = typeof widget.text?.value === "string" ? widget.text.value : void 0;
|
|
1524
1592
|
const TIMER_PASSTHROUGH = /* @__PURE__ */ new Set(["days", "hours", "minutes", "seconds"]);
|
|
1525
|
-
const mapTemplate = (template) => translateSeText(template, null, TIMER_PASSTHROUGH);
|
|
1593
|
+
const mapTemplate = (template) => translateSeText(template, null, TIMER_PASSTHROUGH, void 0, { context: "static" });
|
|
1526
1594
|
const absoluteDate = v.date ?? v.endsAt ?? v.endDate;
|
|
1527
1595
|
const target = absoluteDate ? Date.parse(absoluteDate) : NaN;
|
|
1528
1596
|
const hasValidAbsoluteDate = Number.isFinite(target);
|
|
@@ -1594,7 +1662,7 @@ function mapTimer(widget, ctx) {
|
|
|
1594
1662
|
endAnimation: "",
|
|
1595
1663
|
hideAfter: 0,
|
|
1596
1664
|
highlightColor: "inherit",
|
|
1597
|
-
messageAfterCountdown: translateSeText(v.messageAfterCountdown ?? ""),
|
|
1665
|
+
messageAfterCountdown: translateSeText(v.messageAfterCountdown ?? "", null, void 0, void 0, { context: "static" }),
|
|
1598
1666
|
changeMessageAfter: !!v.changeMessageAfterCountdown,
|
|
1599
1667
|
countDate: hasValidAbsoluteDate && (!!v.exactTime || !!v.datePicker || !!absoluteDate),
|
|
1600
1668
|
date: hasValidAbsoluteDate ? new Date(target).toISOString() : "",
|
|
@@ -1737,28 +1805,47 @@ function mapKappagen(widget, ctx) {
|
|
|
1737
1805
|
ctx
|
|
1738
1806
|
);
|
|
1739
1807
|
}
|
|
1740
|
-
|
|
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) {
|
|
1741
1819
|
const v = widget.variables ?? {};
|
|
1742
1820
|
const count = v.limit ?? v.visibleItems ?? 10;
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
return buildUnit(widget, "loyaltyleaderboard", {
|
|
1746
|
-
// Carry SE text styling so the leaderboard list keeps the streamer's
|
|
1821
|
+
return buildUnit(widget, "viewerprofiles", {
|
|
1822
|
+
// Carry SE text styling so the leaderboard keeps the streamer's
|
|
1747
1823
|
// chosen font / size / color.
|
|
1748
1824
|
css: mapSeTextCssToModuleCss(widget, { fontSize: 18 }),
|
|
1749
1825
|
content: {
|
|
1750
|
-
|
|
1751
|
-
currency,
|
|
1752
|
-
window,
|
|
1826
|
+
sortBy: TOP_LIST_SORT_BY[currency],
|
|
1753
1827
|
count,
|
|
1754
|
-
title,
|
|
1828
|
+
title: TOP_LIST_TITLE[currency],
|
|
1755
1829
|
showTitle: true,
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
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,
|
|
1759
1843
|
itemGap: 8,
|
|
1844
|
+
autoScroll: false,
|
|
1760
1845
|
// Source-side knobs that don't map cleanly survive under the
|
|
1761
|
-
// 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.
|
|
1762
1849
|
importMeta: buildImportMeta(widget, {
|
|
1763
1850
|
period: v.period,
|
|
1764
1851
|
offset: v.offset,
|
|
@@ -1904,6 +1991,47 @@ function mapHypetrain(widget, ctx) {
|
|
|
1904
1991
|
}
|
|
1905
1992
|
}, ctx);
|
|
1906
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
|
+
}
|
|
1907
2035
|
function mapUnsupportedAsText(widget, ctx) {
|
|
1908
2036
|
return buildUnit(widget, "text", {
|
|
1909
2037
|
content: {
|
|
@@ -2303,15 +2431,16 @@ function dispatch(widget, ctx) {
|
|
|
2303
2431
|
return { unit: mapReadout(widget, fallbackVar, ctx), status: "direct", lumiaType: "text" };
|
|
2304
2432
|
}
|
|
2305
2433
|
if (EVENTLIST_TYPES.has(t)) return { unit: mapEventList(widget, ctx), status: "direct", lumiaType: "eventlist" };
|
|
2306
|
-
if (t === "se-widget-top-tippers-list") return { unit:
|
|
2307
|
-
if (t === "se-widget-top-cheerers-list") return { unit:
|
|
2308
|
-
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" };
|
|
2309
2437
|
if (t === "se-widget-credit-roll") return { unit: mapCredits(widget, ctx), status: "partial", lumiaType: "credits" };
|
|
2310
2438
|
if (t === "se-widget-twitch-chat") return { unit: mapChatbox(widget, ctx), status: "partial", lumiaType: "chatbox" };
|
|
2311
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" };
|
|
2312
2440
|
if (t === "se-widget-countdown") return { unit: mapTimer(widget, ctx), status: "partial", lumiaType: "timer" };
|
|
2313
2441
|
if (t === "se-widget-current-song") return { unit: mapNowPlaying(widget, ctx), status: "partial", lumiaType: "nowplaying" };
|
|
2314
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" };
|
|
2315
2444
|
if (t === "se-widget-giveaway") return { unit: mapGiveaway(widget, ctx), status: "direct", lumiaType: "raffle" };
|
|
2316
2445
|
if (t === "se-widget-media-share") return { unit: mapMediaShare(widget, ctx), status: "partial", lumiaType: "songrequest" };
|
|
2317
2446
|
if (t === "se-widget-hype-cup") return { unit: mapHypeCup(widget, ctx), status: "partial", lumiaType: "tipjar" };
|