@lumiastream/ui 0.2.8-alpha.16 → 0.2.8-alpha.18
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.d.ts +1 -1
- package/dist/index.js +473 -376
- package/dist/se-import.d.ts +7 -2
- package/dist/se-import.js +472 -375
- package/package.json +5 -2
package/dist/index.js
CHANGED
|
@@ -3205,10 +3205,11 @@ function defaultLabelForSEWidget(widget) {
|
|
|
3205
3205
|
const tail = words.slice(1).join(" ");
|
|
3206
3206
|
return tail ? `${head} ${tail}` : head;
|
|
3207
3207
|
}
|
|
3208
|
-
function translateSeText(input, listener) {
|
|
3208
|
+
function translateSeText(input, listener, passthrough) {
|
|
3209
3209
|
if (!input) return "";
|
|
3210
3210
|
const ctx = listener ? listenerPlaceholderContext(listener) : {};
|
|
3211
3211
|
return input.replace(/\{([a-zA-Z][a-zA-Z0-9_.]*)\}/g, (_match, raw) => {
|
|
3212
|
+
if (passthrough?.has(raw)) return `{{${raw}}}`;
|
|
3212
3213
|
const mapped = ctx[raw] ?? SE_TO_LUMIA_PLACEHOLDER[raw] ?? raw;
|
|
3213
3214
|
return `{{${mapped}}}`;
|
|
3214
3215
|
});
|
|
@@ -3402,14 +3403,10 @@ function seLayoutUsesPushText(seLayout) {
|
|
|
3402
3403
|
|
|
3403
3404
|
// src/se-import/mappers/build.ts
|
|
3404
3405
|
import { nanoid } from "nanoid";
|
|
3405
|
-
|
|
3406
|
-
function setImportCanvas(canvas) {
|
|
3407
|
-
currentImportCanvas = canvas;
|
|
3408
|
-
}
|
|
3409
|
-
function buildUnit(widget, lumiaType, moduleExtras) {
|
|
3406
|
+
function buildUnit(widget, lumiaType, moduleExtras, ctx) {
|
|
3410
3407
|
const id = nanoid();
|
|
3411
3408
|
const defaults = LUMIA_DEFAULT_SIZES[lumiaType] ?? { width: 400, height: 400 };
|
|
3412
|
-
const bounds = seCssToBounds(widget.css, defaults,
|
|
3409
|
+
const bounds = seCssToBounds(widget.css, defaults, ctx?.canvas);
|
|
3413
3410
|
const layer = {
|
|
3414
3411
|
id,
|
|
3415
3412
|
state: { visible: widget.visible ?? true, locked: widget.locked ?? false },
|
|
@@ -3461,7 +3458,7 @@ function seExtraTextCss(seCss, scrolling) {
|
|
|
3461
3458
|
}
|
|
3462
3459
|
return extra;
|
|
3463
3460
|
}
|
|
3464
|
-
function mapText(widget) {
|
|
3461
|
+
function mapText(widget, ctx) {
|
|
3465
3462
|
const value = translateSeText(widget.text?.value ?? "");
|
|
3466
3463
|
const seCss = widget.text?.css ?? {};
|
|
3467
3464
|
return buildUnit(widget, "text", {
|
|
@@ -3480,17 +3477,17 @@ function mapText(widget) {
|
|
|
3480
3477
|
background: "transparent",
|
|
3481
3478
|
...seExtraTextCss(seCss, widget.text?.scrolling)
|
|
3482
3479
|
}
|
|
3483
|
-
});
|
|
3480
|
+
}, ctx);
|
|
3484
3481
|
}
|
|
3485
|
-
function mapImage(widget) {
|
|
3482
|
+
function mapImage(widget, ctx) {
|
|
3486
3483
|
return buildUnit(widget, "image", {
|
|
3487
3484
|
content: {
|
|
3488
3485
|
src: widget.image?.src ?? "",
|
|
3489
3486
|
loop: true
|
|
3490
3487
|
}
|
|
3491
|
-
});
|
|
3488
|
+
}, ctx);
|
|
3492
3489
|
}
|
|
3493
|
-
function mapVideo(widget) {
|
|
3490
|
+
function mapVideo(widget, ctx) {
|
|
3494
3491
|
return buildUnit(widget, "video", {
|
|
3495
3492
|
content: {
|
|
3496
3493
|
src: widget.video?.src ?? "",
|
|
@@ -3498,10 +3495,10 @@ function mapVideo(widget) {
|
|
|
3498
3495
|
loop: true,
|
|
3499
3496
|
muted: false
|
|
3500
3497
|
}
|
|
3501
|
-
});
|
|
3498
|
+
}, ctx);
|
|
3502
3499
|
}
|
|
3503
3500
|
var LUMIA_SNOW_WEBM = "https://storage.lumiastream.com/overlays/global/seasonal/snow.webm";
|
|
3504
|
-
function mapSnow(widget) {
|
|
3501
|
+
function mapSnow(widget, ctx) {
|
|
3505
3502
|
return buildUnit(widget, "video", {
|
|
3506
3503
|
content: {
|
|
3507
3504
|
src: LUMIA_SNOW_WEBM,
|
|
@@ -3509,9 +3506,9 @@ function mapSnow(widget) {
|
|
|
3509
3506
|
loop: true,
|
|
3510
3507
|
muted: true
|
|
3511
3508
|
}
|
|
3512
|
-
});
|
|
3509
|
+
}, ctx);
|
|
3513
3510
|
}
|
|
3514
|
-
function mapReadout(widget, fallbackVar) {
|
|
3511
|
+
function mapReadout(widget, fallbackVar, ctx) {
|
|
3515
3512
|
const explicitListener = widget.variables?.listener;
|
|
3516
3513
|
const explicit = typeof explicitListener === "string" ? explicitListener : null;
|
|
3517
3514
|
const widgetListener = typeof widget.listener === "string" ? widget.listener : null;
|
|
@@ -3534,7 +3531,7 @@ function mapReadout(widget, fallbackVar) {
|
|
|
3534
3531
|
background: "transparent",
|
|
3535
3532
|
...seExtraTextCss(seCss, widget.text?.scrolling)
|
|
3536
3533
|
}
|
|
3537
|
-
});
|
|
3534
|
+
}, ctx);
|
|
3538
3535
|
}
|
|
3539
3536
|
|
|
3540
3537
|
// src/se-import/mappers/alert.ts
|
|
@@ -3566,13 +3563,14 @@ var SE_EVENT_TO_LUMIA_ALERT = {
|
|
|
3566
3563
|
// mapping prevents misleading "channel-point redemption" alerts.
|
|
3567
3564
|
};
|
|
3568
3565
|
function pickGraphics(g) {
|
|
3569
|
-
|
|
3566
|
+
const DEFAULT_GRAPHIC_VOLUME = 0.5;
|
|
3567
|
+
if (!g) return { type: "image", content: { src: "", loop: false, volume: DEFAULT_GRAPHIC_VOLUME } };
|
|
3570
3568
|
return {
|
|
3571
3569
|
type: g.type === "video" ? "video" : "image",
|
|
3572
3570
|
content: {
|
|
3573
3571
|
src: g.src ?? "",
|
|
3574
3572
|
loop: false,
|
|
3575
|
-
volume: g.volume ??
|
|
3573
|
+
volume: g.volume ?? DEFAULT_GRAPHIC_VOLUME
|
|
3576
3574
|
}
|
|
3577
3575
|
};
|
|
3578
3576
|
}
|
|
@@ -3743,7 +3741,7 @@ function pickBaseAlertFields(event) {
|
|
|
3743
3741
|
const { on: _on, variations: _variations, matchEmptyCondition: _mec, ...base } = event;
|
|
3744
3742
|
return base;
|
|
3745
3743
|
}
|
|
3746
|
-
function mapAlertBox(widget, opts = {}) {
|
|
3744
|
+
function mapAlertBox(widget, opts = {}, ctx) {
|
|
3747
3745
|
const v = widget.variables ?? {};
|
|
3748
3746
|
const events = {};
|
|
3749
3747
|
const eventKeys = opts.onlyEvents ?? ["follower", "subscriber", "tip", "cheer", "raid", "merch", "purchase", "charityCampaignDonation"];
|
|
@@ -3758,9 +3756,9 @@ function mapAlertBox(widget, opts = {}) {
|
|
|
3758
3756
|
if (firstEvent == null) firstEvent = built;
|
|
3759
3757
|
}
|
|
3760
3758
|
const baseAlert = firstEvent ? pickBaseAlertFields(firstEvent) : {};
|
|
3761
|
-
return buildUnit(widget, "alert", { alert: { ...baseAlert, events } });
|
|
3759
|
+
return buildUnit(widget, "alert", { alert: { ...baseAlert, events } }, ctx);
|
|
3762
3760
|
}
|
|
3763
|
-
function mapSingleAlert(widget, lumiaAlertKey) {
|
|
3761
|
+
function mapSingleAlert(widget, lumiaAlertKey, ctx) {
|
|
3764
3762
|
const v = widget.variables ?? {};
|
|
3765
3763
|
const topText = widget.text;
|
|
3766
3764
|
const merged = { ...v };
|
|
@@ -3784,7 +3782,17 @@ function mapSingleAlert(widget, lumiaAlertKey) {
|
|
|
3784
3782
|
[lumiaAlertKey]: built
|
|
3785
3783
|
};
|
|
3786
3784
|
const baseAlert = pickBaseAlertFields(built);
|
|
3787
|
-
return buildUnit(widget, "alert", { alert: { ...baseAlert, events } });
|
|
3785
|
+
return buildUnit(widget, "alert", { alert: { ...baseAlert, events } }, ctx);
|
|
3786
|
+
}
|
|
3787
|
+
|
|
3788
|
+
// src/se-import/mappers/importMeta.ts
|
|
3789
|
+
function buildImportMeta(widget, config) {
|
|
3790
|
+
return {
|
|
3791
|
+
source: "streamelements",
|
|
3792
|
+
widgetType: widget.type,
|
|
3793
|
+
importedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3794
|
+
...config && Object.keys(config).length > 0 ? { config } : {}
|
|
3795
|
+
};
|
|
3788
3796
|
}
|
|
3789
3797
|
|
|
3790
3798
|
// src/se-import/mappers/goal.ts
|
|
@@ -3822,7 +3830,7 @@ function sourceFromListener(listener) {
|
|
|
3822
3830
|
if (listener.startsWith("merch-")) return "merch_orders";
|
|
3823
3831
|
return "custom";
|
|
3824
3832
|
}
|
|
3825
|
-
function mapGoal(widget) {
|
|
3833
|
+
function mapGoal(widget, ctx) {
|
|
3826
3834
|
const v = widget.variables ?? {};
|
|
3827
3835
|
const listener = v.goalListener ?? (typeof widget.listener === "string" ? widget.listener : null);
|
|
3828
3836
|
const charity = charityGoalVariables(widget.type);
|
|
@@ -3862,9 +3870,12 @@ function mapGoal(widget) {
|
|
|
3862
3870
|
border: "solid 1px transparent",
|
|
3863
3871
|
borderRadius: "40px",
|
|
3864
3872
|
highlightColor: "inherit",
|
|
3865
|
-
//
|
|
3866
|
-
|
|
3867
|
-
|
|
3873
|
+
// Source-side knobs without a native Lumia equivalent live under the
|
|
3874
|
+
// generic `importMeta` envelope (see mappers/importMeta.ts).
|
|
3875
|
+
importMeta: buildImportMeta(widget, {
|
|
3876
|
+
sourceListener: listener,
|
|
3877
|
+
simpleDesign: !!v.simpleDesign
|
|
3878
|
+
})
|
|
3868
3879
|
},
|
|
3869
3880
|
css: {
|
|
3870
3881
|
// `borderRadius` / `borderStyle` / `borderWidth` / `borderColor`
|
|
@@ -3883,11 +3894,11 @@ function mapGoal(widget) {
|
|
|
3883
3894
|
textShadow: seTextCss["text-shadow"] ?? "rgb(0, 0, 0) 1px 1px 1px",
|
|
3884
3895
|
background: "transparent"
|
|
3885
3896
|
}
|
|
3886
|
-
});
|
|
3897
|
+
}, ctx);
|
|
3887
3898
|
}
|
|
3888
3899
|
|
|
3889
3900
|
// src/se-import/mappers/custom.ts
|
|
3890
|
-
var
|
|
3901
|
+
var SOURCE_TO_LUMIA_FIELD_TYPE = {
|
|
3891
3902
|
text: "input",
|
|
3892
3903
|
number: "number",
|
|
3893
3904
|
checkbox: "checkbox",
|
|
@@ -3895,19 +3906,45 @@ var SE_FIELD_TYPE_TO_LUMIA = {
|
|
|
3895
3906
|
slider: "slider",
|
|
3896
3907
|
colorpicker: "colorpicker",
|
|
3897
3908
|
fontpicker: "fontpicker",
|
|
3898
|
-
|
|
3909
|
+
// SE's `googleFont` picker is functionally the same as Lumia's native
|
|
3910
|
+
// fontpicker (both load Google Fonts on demand).
|
|
3911
|
+
googleFont: "fontpicker",
|
|
3912
|
+
// Asset uploads land on the native upload types added to Lumia's
|
|
3913
|
+
// ConfigsFieldType. `image` (legacy SE alias) folds into `imageupload`.
|
|
3914
|
+
image: "imageupload",
|
|
3915
|
+
"image-input": "imageupload",
|
|
3916
|
+
"sound-input": "soundupload",
|
|
3917
|
+
"video-input": "videoupload",
|
|
3918
|
+
// Multi-line text → native textarea.
|
|
3919
|
+
textarea: "textarea",
|
|
3920
|
+
// Hidden fields keep their value in `Overlay.data` but don't render a
|
|
3921
|
+
// control; we map to `input` + the BaseConfigField `hidden: true` flag
|
|
3922
|
+
// (applied below in translateConfigs).
|
|
3923
|
+
hidden: "input",
|
|
3924
|
+
// SE-side trigger buttons → native action button.
|
|
3925
|
+
button: "actionbutton"
|
|
3899
3926
|
};
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
|
|
3904
|
-
|
|
3905
|
-
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
|
|
3927
|
+
var RESERVED_FIELD_KEYS = /* @__PURE__ */ new Set(["widgetName", "widgetAuthor", "widgetDuration"]);
|
|
3928
|
+
function translateConfigs(sourceFields) {
|
|
3929
|
+
if (!sourceFields) return [];
|
|
3930
|
+
return Object.entries(sourceFields).filter(([key]) => !RESERVED_FIELD_KEYS.has(key)).map(([key, f]) => {
|
|
3931
|
+
const sourceType = f.type ?? "text";
|
|
3932
|
+
const nativeType = SOURCE_TO_LUMIA_FIELD_TYPE[sourceType] ?? "input";
|
|
3933
|
+
const base = {
|
|
3934
|
+
key,
|
|
3935
|
+
label: f.label ?? key,
|
|
3936
|
+
// The emitted `type` is a native Lumia ConfigsFieldType — no
|
|
3937
|
+
// source-prefixed shadow field rides along. Anything that needs
|
|
3938
|
+
// richer control behavior should land as a new native type.
|
|
3939
|
+
type: nativeType,
|
|
3940
|
+
value: f.value,
|
|
3941
|
+
options: f.options
|
|
3942
|
+
};
|
|
3943
|
+
if (sourceType === "hidden") base.hidden = true;
|
|
3944
|
+
return base;
|
|
3945
|
+
});
|
|
3909
3946
|
}
|
|
3910
|
-
function mapCustom(widget) {
|
|
3947
|
+
function mapCustom(widget, ctx) {
|
|
3911
3948
|
const v = widget.variables ?? {};
|
|
3912
3949
|
const parse = (x) => {
|
|
3913
3950
|
if (x == null) return void 0;
|
|
@@ -3930,7 +3967,7 @@ function mapCustom(widget) {
|
|
|
3930
3967
|
data: fieldData ?? {},
|
|
3931
3968
|
flavor: "streamelements"
|
|
3932
3969
|
}
|
|
3933
|
-
});
|
|
3970
|
+
}, ctx);
|
|
3934
3971
|
}
|
|
3935
3972
|
|
|
3936
3973
|
// src/se-import/mappers/misc.ts
|
|
@@ -3958,7 +3995,7 @@ function paddingShorthand(message) {
|
|
|
3958
3995
|
const px = (n) => `${n ?? 0}px`;
|
|
3959
3996
|
return `${px(t)} ${px(r)} ${px(b)} ${px(l)}`;
|
|
3960
3997
|
}
|
|
3961
|
-
function mapChatbox(widget) {
|
|
3998
|
+
function mapChatbox(widget, ctx) {
|
|
3962
3999
|
const v = widget.variables ?? {};
|
|
3963
4000
|
const textCss = widget.text?.css ?? {};
|
|
3964
4001
|
const messageCss = textCss.message ?? {};
|
|
@@ -4033,16 +4070,19 @@ function mapChatbox(widget) {
|
|
|
4033
4070
|
twitch: { themeConfig: { primaryColor: accentColor, showUsernameColors: true } }
|
|
4034
4071
|
}
|
|
4035
4072
|
} : {},
|
|
4036
|
-
//
|
|
4037
|
-
//
|
|
4038
|
-
//
|
|
4039
|
-
//
|
|
4040
|
-
|
|
4041
|
-
|
|
4042
|
-
|
|
4043
|
-
|
|
4073
|
+
// Source-side knobs preserved under the generic importMeta envelope —
|
|
4074
|
+
// no native Lumia equivalent today. `messageDelay` is per-message
|
|
4075
|
+
// hold, `initialDelay` is the show-after-connect delay, and we
|
|
4076
|
+
// stash the raw highlight block + text css for any future feature
|
|
4077
|
+
// that wants the fine-grained styling.
|
|
4078
|
+
importMeta: buildImportMeta(widget, {
|
|
4079
|
+
messageDelay: v.messageDelay,
|
|
4080
|
+
initialDelay: v.delay,
|
|
4081
|
+
highlightUsers: v.highlight,
|
|
4082
|
+
textCss
|
|
4083
|
+
})
|
|
4044
4084
|
}
|
|
4045
|
-
});
|
|
4085
|
+
}, ctx);
|
|
4046
4086
|
}
|
|
4047
4087
|
var SE_LISTENER_TO_LUMIA_CATEGORY = {
|
|
4048
4088
|
"follower-latest": "follower",
|
|
@@ -4108,7 +4148,7 @@ function computeListenerFilters(seListeners, explicitListener) {
|
|
|
4108
4148
|
if (allowed.size === 0) return [];
|
|
4109
4149
|
return ALL_LUMIA_EVENTLIST_CATEGORIES.filter((c) => !allowed.has(c));
|
|
4110
4150
|
}
|
|
4111
|
-
function mapEventList(widget) {
|
|
4151
|
+
function mapEventList(widget, ctx) {
|
|
4112
4152
|
const v = widget.variables ?? {};
|
|
4113
4153
|
const filters = computeListenerFilters(widget.listeners, widget.listener);
|
|
4114
4154
|
const maxItems = v.visibleEvents ?? v.visibleItems ?? 5;
|
|
@@ -4137,7 +4177,7 @@ function mapEventList(widget) {
|
|
|
4137
4177
|
showDetailedText: false,
|
|
4138
4178
|
hideAlertMessage: false
|
|
4139
4179
|
}
|
|
4140
|
-
});
|
|
4180
|
+
}, ctx);
|
|
4141
4181
|
}
|
|
4142
4182
|
var SE_CREDITS_EVENTS_TO_SHOW = {
|
|
4143
4183
|
mods: false,
|
|
@@ -4277,7 +4317,7 @@ var CREDITS_DEFAULT_TEMPLATES = {
|
|
|
4277
4317
|
topSuperchats: "{{username}} x{{amount}}",
|
|
4278
4318
|
topRedemptions: "{{username}} x{{amount}}"
|
|
4279
4319
|
};
|
|
4280
|
-
function mapCredits(widget) {
|
|
4320
|
+
function mapCredits(widget, ctx) {
|
|
4281
4321
|
const v = widget.variables ?? {};
|
|
4282
4322
|
const itemGap = typeof v.padding === "number" ? v.padding : 8;
|
|
4283
4323
|
const reverseFlow = v.direction === "bottom";
|
|
@@ -4314,12 +4354,15 @@ function mapCredits(widget) {
|
|
|
4314
4354
|
audio: {
|
|
4315
4355
|
content: { src: [], name: [], loop: false, volume: 1 }
|
|
4316
4356
|
},
|
|
4317
|
-
//
|
|
4318
|
-
//
|
|
4319
|
-
//
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
|
|
4357
|
+
// Source-side knobs with no direct Lumia equivalent — preserved
|
|
4358
|
+
// under the generic importMeta envelope. `chosenFilter` would
|
|
4359
|
+
// narrow eventsToShow if we had a clean mapping; `separator` would
|
|
4360
|
+
// join entries but Lumia credits uses sectioned cards instead.
|
|
4361
|
+
importMeta: buildImportMeta(widget, {
|
|
4362
|
+
chosenFilter: v.chosenFilter,
|
|
4363
|
+
separator: v.separator,
|
|
4364
|
+
amount: v.amount
|
|
4365
|
+
})
|
|
4323
4366
|
},
|
|
4324
4367
|
css: {
|
|
4325
4368
|
fontSize: 30,
|
|
@@ -4331,7 +4374,7 @@ function mapCredits(widget) {
|
|
|
4331
4374
|
textAlign: "center",
|
|
4332
4375
|
background: "transparent"
|
|
4333
4376
|
}
|
|
4334
|
-
});
|
|
4377
|
+
}, ctx);
|
|
4335
4378
|
}
|
|
4336
4379
|
function inferSlideshowMediaType(mime, url) {
|
|
4337
4380
|
if (typeof mime === "string") {
|
|
@@ -4350,7 +4393,7 @@ function nameFromUrl(url) {
|
|
|
4350
4393
|
return url;
|
|
4351
4394
|
}
|
|
4352
4395
|
}
|
|
4353
|
-
function mapSlideshow(widget) {
|
|
4396
|
+
function mapSlideshow(widget, ctx) {
|
|
4354
4397
|
const v = widget.variables ?? {};
|
|
4355
4398
|
const items = (v.images ?? []).map((raw) => {
|
|
4356
4399
|
const url = typeof raw === "string" ? raw : raw.url ?? raw.src ?? "";
|
|
@@ -4387,24 +4430,18 @@ function mapSlideshow(widget) {
|
|
|
4387
4430
|
enterAnimationDelay: (seAnim.inDelay ?? 0) * 1e3,
|
|
4388
4431
|
exitAnimationDelay: (seAnim.outDelay ?? 0) * 1e3
|
|
4389
4432
|
},
|
|
4390
|
-
|
|
4391
|
-
|
|
4433
|
+
importMeta: buildImportMeta(widget, {
|
|
4434
|
+
interval: v.interval,
|
|
4435
|
+
rawDuration: v.duration
|
|
4436
|
+
})
|
|
4392
4437
|
}
|
|
4393
|
-
});
|
|
4438
|
+
}, ctx);
|
|
4394
4439
|
}
|
|
4395
|
-
function mapTimer(widget) {
|
|
4440
|
+
function mapTimer(widget, ctx) {
|
|
4396
4441
|
const v = widget.variables ?? {};
|
|
4397
4442
|
const textValue = typeof widget.text?.value === "string" ? widget.text.value : void 0;
|
|
4398
|
-
const
|
|
4399
|
-
|
|
4400
|
-
/\{(days|hours|minutes|seconds)\}/g,
|
|
4401
|
-
"__SE_COUNTDOWN_$1__"
|
|
4402
|
-
);
|
|
4403
|
-
return translateSeText(protectedTemplate).replace(
|
|
4404
|
-
/__SE_COUNTDOWN_(days|hours|minutes|seconds)__/g,
|
|
4405
|
-
"{{$1}}"
|
|
4406
|
-
);
|
|
4407
|
-
};
|
|
4443
|
+
const TIMER_PASSTHROUGH = /* @__PURE__ */ new Set(["days", "hours", "minutes", "seconds"]);
|
|
4444
|
+
const mapTemplate = (template) => translateSeText(template, null, TIMER_PASSTHROUGH);
|
|
4408
4445
|
const absoluteDate = v.date ?? v.endsAt ?? v.endDate;
|
|
4409
4446
|
const target = absoluteDate ? Date.parse(absoluteDate) : NaN;
|
|
4410
4447
|
const hasValidAbsoluteDate = Number.isFinite(target);
|
|
@@ -4476,13 +4513,15 @@ function mapTimer(widget) {
|
|
|
4476
4513
|
changeMessageAfter: !!v.changeMessageAfterCountdown,
|
|
4477
4514
|
countDate: hasValidAbsoluteDate && (!!v.exactTime || !!v.datePicker || !!absoluteDate),
|
|
4478
4515
|
date: hasValidAbsoluteDate ? new Date(target).toISOString() : "",
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4516
|
+
importMeta: buildImportMeta(widget, {
|
|
4517
|
+
rawDate: absoluteDate,
|
|
4518
|
+
datePicker: v.datePicker,
|
|
4519
|
+
exactTime: v.exactTime
|
|
4520
|
+
})
|
|
4482
4521
|
}
|
|
4483
|
-
});
|
|
4522
|
+
}, ctx);
|
|
4484
4523
|
}
|
|
4485
|
-
function mapNowPlaying(widget) {
|
|
4524
|
+
function mapNowPlaying(widget, ctx) {
|
|
4486
4525
|
const v = widget.variables ?? {};
|
|
4487
4526
|
const source = v.provider === "youtube" ? "youtubemusic" : "spotify";
|
|
4488
4527
|
return buildUnit(widget, "nowplaying", {
|
|
@@ -4498,7 +4537,7 @@ function mapNowPlaying(widget) {
|
|
|
4498
4537
|
showImg: true,
|
|
4499
4538
|
oneLineTitle: false,
|
|
4500
4539
|
highlightColor: "inherit",
|
|
4501
|
-
|
|
4540
|
+
importMeta: buildImportMeta(widget, { simpleDesign: !!v.simpleDesign })
|
|
4502
4541
|
},
|
|
4503
4542
|
css: {
|
|
4504
4543
|
borderRadius: "0px",
|
|
@@ -4512,9 +4551,9 @@ function mapNowPlaying(widget) {
|
|
|
4512
4551
|
textShadow: "rgb(0, 0, 0) 1px 1px 1px",
|
|
4513
4552
|
background: "transparent"
|
|
4514
4553
|
}
|
|
4515
|
-
});
|
|
4554
|
+
}, ctx);
|
|
4516
4555
|
}
|
|
4517
|
-
function mapMediaShare(widget) {
|
|
4556
|
+
function mapMediaShare(widget, ctx) {
|
|
4518
4557
|
const v = widget.variables ?? {};
|
|
4519
4558
|
return buildUnit(widget, "songrequest", {
|
|
4520
4559
|
content: {
|
|
@@ -4543,9 +4582,10 @@ function mapMediaShare(widget) {
|
|
|
4543
4582
|
playerVisible: false,
|
|
4544
4583
|
defaultVolume: 70,
|
|
4545
4584
|
autoAdvanceOnEnd: true,
|
|
4546
|
-
//
|
|
4547
|
-
// it (
|
|
4548
|
-
|
|
4585
|
+
// Source-side knob preserved for round-trip provenance; Lumia
|
|
4586
|
+
// ignores it at runtime (auto-advance fires on
|
|
4587
|
+
// `durationSeconds + grace`).
|
|
4588
|
+
importMeta: buildImportMeta(widget, { timeLeft: v.timeLeft === true })
|
|
4549
4589
|
},
|
|
4550
4590
|
css: {
|
|
4551
4591
|
fontFamily: "Roboto",
|
|
@@ -4555,7 +4595,7 @@ function mapMediaShare(widget) {
|
|
|
4555
4595
|
background: "transparent",
|
|
4556
4596
|
fontSize: 18
|
|
4557
4597
|
}
|
|
4558
|
-
});
|
|
4598
|
+
}, ctx);
|
|
4559
4599
|
}
|
|
4560
4600
|
var SE_KAPPAGEN_EVENT_TO_LUMIA_ALERT = {
|
|
4561
4601
|
follower: "twitch-follower",
|
|
@@ -4578,7 +4618,7 @@ var SE_KAPPAGEN_EVENT_TO_LUMIA_ALERT = {
|
|
|
4578
4618
|
// All three drop silently; the rest of the widget (size, spawn zone,
|
|
4579
4619
|
// per-event flags for the remaining events) still imports.
|
|
4580
4620
|
};
|
|
4581
|
-
function mapKappagen(widget) {
|
|
4621
|
+
function mapKappagen(widget, ctx) {
|
|
4582
4622
|
const v = widget.variables ?? {};
|
|
4583
4623
|
const eventsFromNested = v.eventsConfig ? {
|
|
4584
4624
|
subscriber: v.eventsConfig.subscriber?.enabled ?? true,
|
|
@@ -4619,21 +4659,25 @@ function mapKappagen(widget) {
|
|
|
4619
4659
|
ffz: v.ffz !== false,
|
|
4620
4660
|
sevenTv: v.sevenTv !== false
|
|
4621
4661
|
},
|
|
4622
|
-
//
|
|
4623
|
-
|
|
4624
|
-
|
|
4625
|
-
|
|
4626
|
-
|
|
4627
|
-
|
|
4628
|
-
|
|
4629
|
-
|
|
4630
|
-
|
|
4662
|
+
// Source-side knobs preserved under the generic importMeta
|
|
4663
|
+
// envelope — no Lumia rendering for these today.
|
|
4664
|
+
importMeta: buildImportMeta(widget, {
|
|
4665
|
+
mode: v.mode,
|
|
4666
|
+
cheersplosions: !!v.cheersplosions,
|
|
4667
|
+
emotesplosion: v.emotesplosion,
|
|
4668
|
+
emotesplosionDir: v.emotesplosiondir,
|
|
4669
|
+
emotesplosionDuration: v.emotesplosionduration,
|
|
4670
|
+
spawnZone: v.spawnzone,
|
|
4671
|
+
fullscreen: !!v.fullscreen,
|
|
4672
|
+
direction: v.direction
|
|
4673
|
+
})
|
|
4631
4674
|
},
|
|
4632
4675
|
css: { background: "transparent" }
|
|
4633
|
-
}
|
|
4676
|
+
},
|
|
4677
|
+
ctx
|
|
4634
4678
|
);
|
|
4635
4679
|
}
|
|
4636
|
-
function mapLeaderboardFromVariables(widget, currency) {
|
|
4680
|
+
function mapLeaderboardFromVariables(widget, currency, ctx) {
|
|
4637
4681
|
const v = widget.variables ?? {};
|
|
4638
4682
|
const count = v.limit ?? v.visibleItems ?? 10;
|
|
4639
4683
|
const window = v.period === "weekly" ? "week" : v.period === "monthly" ? "month" : "alltime";
|
|
@@ -4650,15 +4694,17 @@ function mapLeaderboardFromVariables(widget, currency) {
|
|
|
4650
4694
|
showCurrentPoints: false,
|
|
4651
4695
|
showAllTimePoints: true,
|
|
4652
4696
|
itemGap: 8,
|
|
4653
|
-
//
|
|
4654
|
-
//
|
|
4655
|
-
|
|
4656
|
-
|
|
4657
|
-
|
|
4697
|
+
// Source-side knobs that don't map cleanly survive under the
|
|
4698
|
+
// generic importMeta envelope so the user can re-introspect.
|
|
4699
|
+
importMeta: buildImportMeta(widget, {
|
|
4700
|
+
period: v.period,
|
|
4701
|
+
offset: v.offset,
|
|
4702
|
+
layout: v.layout
|
|
4703
|
+
})
|
|
4658
4704
|
}
|
|
4659
|
-
});
|
|
4705
|
+
}, ctx);
|
|
4660
4706
|
}
|
|
4661
|
-
function mapGiveaway(widget) {
|
|
4707
|
+
function mapGiveaway(widget, ctx) {
|
|
4662
4708
|
const v = widget.variables ?? {};
|
|
4663
4709
|
const primary = v.colors?.primary ?? "#393853";
|
|
4664
4710
|
const accent = v.colors?.accent ?? "#FF4076";
|
|
@@ -4684,8 +4730,6 @@ function mapGiveaway(widget) {
|
|
|
4684
4730
|
// preference via provenance — a future renderer can read it without us
|
|
4685
4731
|
// having lied about the toggle. Same for `displayTicketsPurchased`.
|
|
4686
4732
|
hideParticipantOnStart: false,
|
|
4687
|
-
se_displayEntrants: v.displayEntrants !== false,
|
|
4688
|
-
se_displayTicketsPurchased: v.displayTicketsPurchased !== false,
|
|
4689
4733
|
removeWhenRedeemed: false,
|
|
4690
4734
|
showWinningModal: true,
|
|
4691
4735
|
playSoundFx: true,
|
|
@@ -4702,14 +4746,20 @@ function mapGiveaway(widget) {
|
|
|
4702
4746
|
raffleEndedText: "The raffle has ended, and the selection of the winner will take place shortly.",
|
|
4703
4747
|
raffleTimerText: "Time since raffle started",
|
|
4704
4748
|
raffleCountdownText: "Raffle will end after",
|
|
4705
|
-
//
|
|
4706
|
-
|
|
4707
|
-
|
|
4708
|
-
|
|
4709
|
-
|
|
4710
|
-
|
|
4749
|
+
// Source-side knobs with no Lumia equivalent — preserved under
|
|
4750
|
+
// the generic importMeta envelope. `displayEntrants` / `displayTicketsPurchased`
|
|
4751
|
+
// are SE's persistent "never show" toggles; Lumia has no equivalent
|
|
4752
|
+
// today (its `hideParticipantOnStart` is transient), so we preserve
|
|
4753
|
+
// the preference for a future renderer.
|
|
4754
|
+
importMeta: buildImportMeta(widget, {
|
|
4755
|
+
timeout: v.timeout,
|
|
4756
|
+
enableTimeout: v.enableTimeOut,
|
|
4757
|
+
displayEntrants: v.displayEntrants,
|
|
4758
|
+
displayTicketsPurchased: v.displayTicketsPurchased,
|
|
4759
|
+
imageStyleWidth: v.imageStyle?.width
|
|
4760
|
+
})
|
|
4711
4761
|
}
|
|
4712
|
-
});
|
|
4762
|
+
}, ctx);
|
|
4713
4763
|
}
|
|
4714
4764
|
var HYPETRAIN_LISTENER_KEYS = /* @__PURE__ */ new Set([
|
|
4715
4765
|
"hypetrain-latest",
|
|
@@ -4740,7 +4790,7 @@ function isHypetrainCustomWidget(widget) {
|
|
|
4740
4790
|
if (enabled.length === 0) return false;
|
|
4741
4791
|
return enabled.every((listener) => HYPETRAIN_LISTENER_KEYS.has(listener));
|
|
4742
4792
|
}
|
|
4743
|
-
function mapHypetrain(widget) {
|
|
4793
|
+
function mapHypetrain(widget, ctx) {
|
|
4744
4794
|
const v = widget.variables ?? {};
|
|
4745
4795
|
return buildUnit(widget, "hypetrain", {
|
|
4746
4796
|
content: {
|
|
@@ -4777,15 +4827,16 @@ function mapHypetrain(widget) {
|
|
|
4777
4827
|
trainLocoImageUrl: "https://storage.lumiastream.com/overlays/global/hypetrain/train_loco_default.png",
|
|
4778
4828
|
rocketImageUrl: "https://storage.lumiastream.com/overlays/global/hypetrain/rocket_default.png",
|
|
4779
4829
|
infernoImageUrl: "https://storage.lumiastream.com/overlays/global/hypetrain/inferno_default.png",
|
|
4780
|
-
//
|
|
4781
|
-
//
|
|
4782
|
-
//
|
|
4783
|
-
//
|
|
4784
|
-
|
|
4830
|
+
// Source config preserved verbatim under the generic importMeta
|
|
4831
|
+
// envelope — no Lumia equivalent for most of these knobs
|
|
4832
|
+
// (per-widget animation tunables, sound URLs the streamer might
|
|
4833
|
+
// want to migrate manually, etc.). Users can inspect via the
|
|
4834
|
+
// overlay JSON if they want to recreate a specific look.
|
|
4835
|
+
importMeta: buildImportMeta(widget, { variables: v })
|
|
4785
4836
|
}
|
|
4786
|
-
});
|
|
4837
|
+
}, ctx);
|
|
4787
4838
|
}
|
|
4788
|
-
function mapUnsupportedAsText(widget) {
|
|
4839
|
+
function mapUnsupportedAsText(widget, ctx) {
|
|
4789
4840
|
return buildUnit(widget, "text", {
|
|
4790
4841
|
content: {
|
|
4791
4842
|
value: `[Unsupported SE widget: ${widget.type}]`,
|
|
@@ -4799,7 +4850,7 @@ function mapUnsupportedAsText(widget) {
|
|
|
4799
4850
|
fontWeight: "bold",
|
|
4800
4851
|
background: "rgba(0,0,0,0.4)"
|
|
4801
4852
|
}
|
|
4802
|
-
});
|
|
4853
|
+
}, ctx);
|
|
4803
4854
|
}
|
|
4804
4855
|
|
|
4805
4856
|
// src/se-import/mappers/hypecup.ts
|
|
@@ -4869,7 +4920,7 @@ function categoriesFromListeners(listeners) {
|
|
|
4869
4920
|
if (events.subs) events.resubs = true;
|
|
4870
4921
|
return events;
|
|
4871
4922
|
}
|
|
4872
|
-
function mapHypeCup(widget) {
|
|
4923
|
+
function mapHypeCup(widget, ctx) {
|
|
4873
4924
|
const v = widget.variables ?? {};
|
|
4874
4925
|
const widgetWidth = typeof v.width === "number" && v.width > 0 ? v.width : parsePx(widget.css?.width);
|
|
4875
4926
|
const widgetHeight = typeof v.height === "number" && v.height > 0 ? v.height : parsePx(widget.css?.height);
|
|
@@ -4916,18 +4967,21 @@ function mapHypeCup(widget) {
|
|
|
4916
4967
|
// can enable in Settings; SE has no equivalent so nothing to lift.
|
|
4917
4968
|
goal: { active: false, currency: "tips", target: 100, current: 0, readoutTemplate: "{{current}} / {{target}}", textColor: "#ffffff", hitAt: null },
|
|
4918
4969
|
break: { enabled: false, command: "!breakjar", rebuildDelay: 5e3 },
|
|
4919
|
-
//
|
|
4920
|
-
//
|
|
4921
|
-
// physics tweaks).
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
|
|
4925
|
-
|
|
4970
|
+
// Source-side knobs without a Lumia equivalent — preserved under
|
|
4971
|
+
// the generic `importMeta` envelope for future phases (per-cup
|
|
4972
|
+
// polygons + enter/movement physics tweaks). The per-event
|
|
4973
|
+
// sprite sets were already lifted into native `sprites` above.
|
|
4974
|
+
importMeta: buildImportMeta(widget, {
|
|
4975
|
+
enterFrom: v.enterFrom ?? null,
|
|
4976
|
+
preventMovement: !!v.preventMovement,
|
|
4977
|
+
fullscreen: !!v.fullscreen,
|
|
4978
|
+
cups: v.cups ?? null
|
|
4979
|
+
})
|
|
4926
4980
|
},
|
|
4927
4981
|
css: {
|
|
4928
4982
|
background: "transparent"
|
|
4929
4983
|
}
|
|
4930
|
-
});
|
|
4984
|
+
}, ctx);
|
|
4931
4985
|
}
|
|
4932
4986
|
function clamp01(n) {
|
|
4933
4987
|
if (!Number.isFinite(n)) return 0.5;
|
|
@@ -4974,7 +5028,7 @@ function listenerIsOn(listeners, key) {
|
|
|
4974
5028
|
if (Array.isArray(listeners)) return listeners.includes(key);
|
|
4975
5029
|
return listeners[key] === true;
|
|
4976
5030
|
}
|
|
4977
|
-
function mapStreamBoss(widget) {
|
|
5031
|
+
function mapStreamBoss(widget, ctx) {
|
|
4978
5032
|
const v = widget.variables ?? {};
|
|
4979
5033
|
const baseHp = typeof v.baseHP === "number" && v.baseHP > 0 ? Math.round(v.baseHP) : 1e3;
|
|
4980
5034
|
const seDamage = v.damage ?? {};
|
|
@@ -5041,22 +5095,24 @@ function mapStreamBoss(widget) {
|
|
|
5041
5095
|
bossNameColor: "",
|
|
5042
5096
|
botChat: "off",
|
|
5043
5097
|
chatAsSelf: false,
|
|
5044
|
-
//
|
|
5045
|
-
// have direct Lumia analogs today:
|
|
5046
|
-
// - `right`
|
|
5047
|
-
//
|
|
5048
|
-
// - `
|
|
5049
|
-
//
|
|
5050
|
-
// - `mode`
|
|
5051
|
-
|
|
5052
|
-
|
|
5053
|
-
|
|
5054
|
-
|
|
5098
|
+
// Source-side knobs preserved as provenance for future passes.
|
|
5099
|
+
// None have direct Lumia analogs today:
|
|
5100
|
+
// - `right` anchors the boss to the right edge of its
|
|
5101
|
+
// bounds; Lumia uses layer position instead.
|
|
5102
|
+
// - `bossNamePosition` SE-controlled vertical name position; our
|
|
5103
|
+
// themes don't expose that level of tweaking.
|
|
5104
|
+
// - `mode` source-side gameplay mode hint ('overkill').
|
|
5105
|
+
importMeta: buildImportMeta(widget, {
|
|
5106
|
+
right: !!v.right,
|
|
5107
|
+
bossNamePosition: typeof v.bossName === "string" ? v.bossName : null,
|
|
5108
|
+
mode: typeof v.mode === "string" ? v.mode : null,
|
|
5109
|
+
showName: v.showName !== false
|
|
5110
|
+
})
|
|
5055
5111
|
},
|
|
5056
5112
|
css: {
|
|
5057
5113
|
background: "transparent"
|
|
5058
5114
|
}
|
|
5059
|
-
});
|
|
5115
|
+
}, ctx);
|
|
5060
5116
|
if (theme === "card") {
|
|
5061
5117
|
const MIN_CARD_WIDTH = 480;
|
|
5062
5118
|
const MIN_CARD_HEIGHT = 110;
|
|
@@ -5067,25 +5123,6 @@ function mapStreamBoss(widget) {
|
|
|
5067
5123
|
}
|
|
5068
5124
|
|
|
5069
5125
|
// src/se-import/feature-flags.ts
|
|
5070
|
-
var SE_IMPORT_FLAGS = {
|
|
5071
|
-
// (Removed) IMPORT_CUSTOM_EMBED: custom-event-list now routes to native eventlist
|
|
5072
|
-
// by default with a one-click "switch to custom HTML" option in the review step.
|
|
5073
|
-
// (Removed) IMPORT_MERCH_GOAL: Fourthwall integration provides session/total order
|
|
5074
|
-
// counters now (see fourthwall.manager.ts ORDER_PLACED handler); merch goal maps
|
|
5075
|
-
// to {{fourthwall_total_*}} via the goal mapper (per-listener: orders → order_count,
|
|
5076
|
-
// items → items_count, total → order_amount).
|
|
5077
|
-
// (Removed) IMPORT_SEASONAL: seasonal widgets now route per-widget into existing
|
|
5078
|
-
// modules (image / slideshow / video) rather than a single approximate canvas-
|
|
5079
|
-
// particle template. See dispatcher.ts SEASONAL_IMAGE_TYPES comment for the
|
|
5080
|
-
// rationale and /SE/05-widget-to-module-mapping.md §D for the current mapping.
|
|
5081
|
-
// (Removed) IMPORT_MERCH_PRODUCTS: the merch-products-rotator now imports as an
|
|
5082
|
-
// empty slideshow with a friendly layer title prompting the user to add product
|
|
5083
|
-
// images manually. This is a degraded but honest UX — better than a placeholder
|
|
5084
|
-
// text layer for the (rare) streamer who uses SE's merch rotator. A Fourthwall
|
|
5085
|
-
// catalog fetcher would unlock fully-automatic import, but that's a multi-day
|
|
5086
|
-
// project gated on a new Fourthwall API auth scope, an endpoint, and a refresh
|
|
5087
|
-
// cadence.
|
|
5088
|
-
};
|
|
5089
5126
|
function isWidgetFlaggedOff(_seType) {
|
|
5090
5127
|
return false;
|
|
5091
5128
|
}
|
|
@@ -5140,7 +5177,7 @@ var GOAL_TYPES = /* @__PURE__ */ new Set([
|
|
|
5140
5177
|
"se-widget-merch-goal"
|
|
5141
5178
|
]);
|
|
5142
5179
|
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"]);
|
|
5143
|
-
function dispatch(widget) {
|
|
5180
|
+
function dispatch(widget, ctx) {
|
|
5144
5181
|
const t = widget.type;
|
|
5145
5182
|
console.log("[SE-IMPORT] dispatch widget", {
|
|
5146
5183
|
seType: t,
|
|
@@ -5151,46 +5188,46 @@ function dispatch(widget) {
|
|
|
5151
5188
|
hasCss: !!widget.css
|
|
5152
5189
|
});
|
|
5153
5190
|
if (isWidgetFlaggedOff(t)) {
|
|
5154
|
-
return { unit: mapUnsupportedAsText(widget), status: "placeholder", lumiaType: "text", flaggedOff: true };
|
|
5191
|
+
return { unit: mapUnsupportedAsText(widget, ctx), status: "placeholder", lumiaType: "text", flaggedOff: true };
|
|
5155
5192
|
}
|
|
5156
|
-
if (t === "text") return { unit: mapText(widget), status: "direct", lumiaType: "text" };
|
|
5157
|
-
if (t === "image") return { unit: mapImage(widget), status: "direct", lumiaType: "image" };
|
|
5158
|
-
if (t === "video") return { unit: mapVideo(widget), status: "direct", lumiaType: "video" };
|
|
5193
|
+
if (t === "text") return { unit: mapText(widget, ctx), status: "direct", lumiaType: "text" };
|
|
5194
|
+
if (t === "image") return { unit: mapImage(widget, ctx), status: "direct", lumiaType: "image" };
|
|
5195
|
+
if (t === "video") return { unit: mapVideo(widget, ctx), status: "direct", lumiaType: "video" };
|
|
5159
5196
|
if (t in READOUT_TYPES) {
|
|
5160
|
-
return { unit: mapReadout(widget, READOUT_TYPES[t]), status: "direct", lumiaType: "text" };
|
|
5197
|
+
return { unit: mapReadout(widget, READOUT_TYPES[t], ctx), status: "direct", lumiaType: "text" };
|
|
5161
5198
|
}
|
|
5162
|
-
if (GOAL_TYPES.has(t)) return { unit: mapGoal(widget), status: "direct", lumiaType: "goal" };
|
|
5199
|
+
if (GOAL_TYPES.has(t)) return { unit: mapGoal(widget, ctx), status: "direct", lumiaType: "goal" };
|
|
5163
5200
|
if ((t === "se-widget-alert-box" || t === "se-widget-alert-box-merch") && hasSECustomCode(widget)) {
|
|
5164
|
-
return { unit: mapUnsupportedAsText(widget), status: "placeholder", lumiaType: "text", flaggedOff: true };
|
|
5165
|
-
}
|
|
5166
|
-
if (t === "se-widget-alert-box") return { unit: mapAlertBox(widget), status: "direct", lumiaType: "alert" };
|
|
5167
|
-
if (t === "se-widget-alert-box-merch") return { unit: mapAlertBox(widget, { onlyEvents: ["merch"] }), status: "direct", lumiaType: "alert" };
|
|
5168
|
-
if (t === "se-widget-donor-drive-alert") return { unit: mapSingleAlert(widget, "donordrive-donation"), status: "direct", lumiaType: "alert" };
|
|
5169
|
-
if (t === "se-widget-extralife-alert") return { unit: mapSingleAlert(widget, "extralife-donation"), status: "direct", lumiaType: "alert" };
|
|
5170
|
-
if (t === "se-widget-tiltify-alert") return { unit: mapSingleAlert(widget, "tiltify-campaignDonation"), status: "direct", lumiaType: "alert" };
|
|
5171
|
-
if (EVENTLIST_TYPES.has(t)) return { unit: mapEventList(widget), status: "direct", lumiaType: "eventlist" };
|
|
5172
|
-
if (t === "se-widget-top-tippers-list") return { unit: mapLeaderboardFromVariables(widget, "tips"), status: "direct", lumiaType: "loyaltyleaderboard" };
|
|
5173
|
-
if (t === "se-widget-top-cheerers-list") return { unit: mapLeaderboardFromVariables(widget, "cheers"), status: "direct", lumiaType: "loyaltyleaderboard" };
|
|
5174
|
-
if (t === "se-widget-top-gifters-list") return { unit: mapLeaderboardFromVariables(widget, "gifts"), status: "direct", lumiaType: "loyaltyleaderboard" };
|
|
5175
|
-
if (t === "se-widget-credit-roll") return { unit: mapCredits(widget), status: "partial", lumiaType: "credits" };
|
|
5176
|
-
if (t === "se-widget-twitch-chat") return { unit: mapChatbox(widget), status: "partial", lumiaType: "chatbox" };
|
|
5177
|
-
if (t === "se-widget-image-rotator" || t === "se-widget-merch-products-rotator") return { unit: mapSlideshow(widget), status: t === "se-widget-merch-products-rotator" ? "partial" : "direct", lumiaType: "slideshow" };
|
|
5178
|
-
if (t === "se-widget-countdown") return { unit: mapTimer(widget), status: "partial", lumiaType: "timer" };
|
|
5179
|
-
if (t === "se-widget-current-song") return { unit: mapNowPlaying(widget), status: "partial", lumiaType: "nowplaying" };
|
|
5180
|
-
if (t === "se-widget-kappagen") return { unit: mapKappagen(widget), status: "partial", lumiaType: "emotealert" };
|
|
5181
|
-
if (t === "se-widget-giveaway") return { unit: mapGiveaway(widget), status: "direct", lumiaType: "raffle" };
|
|
5182
|
-
if (t === "se-widget-media-share") return { unit: mapMediaShare(widget), status: "partial", lumiaType: "songrequest" };
|
|
5183
|
-
if (t === "se-widget-hype-cup") return { unit: mapHypeCup(widget), status: "partial", lumiaType: "tipjar" };
|
|
5184
|
-
if (t === "se-widget-bit-boss") return { unit: mapStreamBoss(widget), status: "partial", lumiaType: "streamboss" };
|
|
5185
|
-
if (t === "se-widget-hype-train") return { unit: mapHypetrain(widget), status: "partial", lumiaType: "hypetrain" };
|
|
5201
|
+
return { unit: mapUnsupportedAsText(widget, ctx), status: "placeholder", lumiaType: "text", flaggedOff: true };
|
|
5202
|
+
}
|
|
5203
|
+
if (t === "se-widget-alert-box") return { unit: mapAlertBox(widget, {}, ctx), status: "direct", lumiaType: "alert" };
|
|
5204
|
+
if (t === "se-widget-alert-box-merch") return { unit: mapAlertBox(widget, { onlyEvents: ["merch"] }, ctx), status: "direct", lumiaType: "alert" };
|
|
5205
|
+
if (t === "se-widget-donor-drive-alert") return { unit: mapSingleAlert(widget, "donordrive-donation", ctx), status: "direct", lumiaType: "alert" };
|
|
5206
|
+
if (t === "se-widget-extralife-alert") return { unit: mapSingleAlert(widget, "extralife-donation", ctx), status: "direct", lumiaType: "alert" };
|
|
5207
|
+
if (t === "se-widget-tiltify-alert") return { unit: mapSingleAlert(widget, "tiltify-campaignDonation", ctx), status: "direct", lumiaType: "alert" };
|
|
5208
|
+
if (EVENTLIST_TYPES.has(t)) return { unit: mapEventList(widget, ctx), status: "direct", lumiaType: "eventlist" };
|
|
5209
|
+
if (t === "se-widget-top-tippers-list") return { unit: mapLeaderboardFromVariables(widget, "tips", ctx), status: "direct", lumiaType: "loyaltyleaderboard" };
|
|
5210
|
+
if (t === "se-widget-top-cheerers-list") return { unit: mapLeaderboardFromVariables(widget, "cheers", ctx), status: "direct", lumiaType: "loyaltyleaderboard" };
|
|
5211
|
+
if (t === "se-widget-top-gifters-list") return { unit: mapLeaderboardFromVariables(widget, "gifts", ctx), status: "direct", lumiaType: "loyaltyleaderboard" };
|
|
5212
|
+
if (t === "se-widget-credit-roll") return { unit: mapCredits(widget, ctx), status: "partial", lumiaType: "credits" };
|
|
5213
|
+
if (t === "se-widget-twitch-chat") return { unit: mapChatbox(widget, ctx), status: "partial", lumiaType: "chatbox" };
|
|
5214
|
+
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" };
|
|
5215
|
+
if (t === "se-widget-countdown") return { unit: mapTimer(widget, ctx), status: "partial", lumiaType: "timer" };
|
|
5216
|
+
if (t === "se-widget-current-song") return { unit: mapNowPlaying(widget, ctx), status: "partial", lumiaType: "nowplaying" };
|
|
5217
|
+
if (t === "se-widget-kappagen") return { unit: mapKappagen(widget, ctx), status: "partial", lumiaType: "emotealert" };
|
|
5218
|
+
if (t === "se-widget-giveaway") return { unit: mapGiveaway(widget, ctx), status: "direct", lumiaType: "raffle" };
|
|
5219
|
+
if (t === "se-widget-media-share") return { unit: mapMediaShare(widget, ctx), status: "partial", lumiaType: "songrequest" };
|
|
5220
|
+
if (t === "se-widget-hype-cup") return { unit: mapHypeCup(widget, ctx), status: "partial", lumiaType: "tipjar" };
|
|
5221
|
+
if (t === "se-widget-bit-boss") return { unit: mapStreamBoss(widget, ctx), status: "partial", lumiaType: "streamboss" };
|
|
5222
|
+
if (t === "se-widget-hype-train") return { unit: mapHypetrain(widget, ctx), status: "partial", lumiaType: "hypetrain" };
|
|
5186
5223
|
if (t === "se-widget-custom-event-list" && isHypetrainCustomWidget(widget)) {
|
|
5187
|
-
return { unit: mapHypetrain(widget), status: "partial", lumiaType: "hypetrain" };
|
|
5224
|
+
return { unit: mapHypetrain(widget, ctx), status: "partial", lumiaType: "hypetrain" };
|
|
5188
5225
|
}
|
|
5189
|
-
if (t === "se-widget-custom-event-list") return { unit: mapCustom(widget), status: "partial", lumiaType: "custom" };
|
|
5190
|
-
if (t === "se-widget-snow") return { unit: mapSnow(widget), status: "direct", lumiaType: "video" };
|
|
5191
|
-
if (t === "se-widget-halloween-2019") return { unit: mapSlideshow(widget), status: "direct", lumiaType: "slideshow" };
|
|
5192
|
-
if (SEASONAL_IMAGE_TYPES.has(t)) return { unit: mapImage(widget), status: "direct", lumiaType: "image" };
|
|
5193
|
-
return { unit: mapUnsupportedAsText(widget), status: "placeholder", lumiaType: "text" };
|
|
5226
|
+
if (t === "se-widget-custom-event-list") return { unit: mapCustom(widget, ctx), status: "partial", lumiaType: "custom" };
|
|
5227
|
+
if (t === "se-widget-snow") return { unit: mapSnow(widget, ctx), status: "direct", lumiaType: "video" };
|
|
5228
|
+
if (t === "se-widget-halloween-2019") return { unit: mapSlideshow(widget, ctx), status: "direct", lumiaType: "slideshow" };
|
|
5229
|
+
if (SEASONAL_IMAGE_TYPES.has(t)) return { unit: mapImage(widget, ctx), status: "direct", lumiaType: "image" };
|
|
5230
|
+
return { unit: mapUnsupportedAsText(widget, ctx), status: "placeholder", lumiaType: "text" };
|
|
5194
5231
|
}
|
|
5195
5232
|
function recordCoverage(coverage, result, seType) {
|
|
5196
5233
|
const existing = coverage.mappings.find((m) => m.seType === seType);
|
|
@@ -5248,11 +5285,19 @@ function rewriteAssetURLs(overlay, mapping) {
|
|
|
5248
5285
|
settings: JSON.parse(rewritten)
|
|
5249
5286
|
};
|
|
5250
5287
|
}
|
|
5288
|
+
var MAX_MIRROR_ASSET_BYTES = 100 * 1024 * 1024;
|
|
5251
5289
|
async function mirrorOneAsset(url, upload, signal) {
|
|
5252
5290
|
const res = await fetch(url, { mode: "cors", signal });
|
|
5253
5291
|
if (!res.ok) throw new Error(`Source returned HTTP ${res.status}`);
|
|
5292
|
+
const declared = Number(res.headers.get("content-length"));
|
|
5293
|
+
if (Number.isFinite(declared) && declared > MAX_MIRROR_ASSET_BYTES) {
|
|
5294
|
+
throw new Error(`Asset is ${(declared / (1024 * 1024)).toFixed(1)} MB \u2014 over the ${MAX_MIRROR_ASSET_BYTES / (1024 * 1024)} MB import cap.`);
|
|
5295
|
+
}
|
|
5254
5296
|
const blob = await res.blob();
|
|
5255
5297
|
if (signal?.aborted) throw new DOMException("Aborted", "AbortError");
|
|
5298
|
+
if (blob.size > MAX_MIRROR_ASSET_BYTES) {
|
|
5299
|
+
throw new Error(`Asset is ${(blob.size / (1024 * 1024)).toFixed(1)} MB \u2014 over the ${MAX_MIRROR_ASSET_BYTES / (1024 * 1024)} MB import cap.`);
|
|
5300
|
+
}
|
|
5256
5301
|
const filename = filenameFromURL(url);
|
|
5257
5302
|
const type = blob.type || guessMime(filename);
|
|
5258
5303
|
const file = new File([blob], filename, { type });
|
|
@@ -5965,13 +6010,19 @@ function CustomOverlayPreview({
|
|
|
5965
6010
|
}) {
|
|
5966
6011
|
const srcdoc = useMemo5(() => {
|
|
5967
6012
|
const safeData = JSON.stringify(data ?? {});
|
|
5968
|
-
const safeJs =
|
|
6013
|
+
const safeJs = (js || "").replace(/<\/script>/gi, "<\\/script>");
|
|
5969
6014
|
const safeHtml = (html || "").replace(/<\/script>/gi, "<\\/script>");
|
|
5970
6015
|
const safeCss = (css || "").replace(/<\/style>/gi, "<\\/style>");
|
|
5971
6016
|
return `<!DOCTYPE html><html><head><meta charset="utf-8"/><style>html,body{margin:0;padding:0;width:100%;height:100%;overflow:hidden;background:transparent;}${safeCss}</style></head><body>${safeHtml}<script>
|
|
5972
6017
|
window.DATA = ${safeData};
|
|
5973
6018
|
window.Overlay = window.Overlay || { on:()=>{}, callCommand:()=>Promise.resolve(), chatbot:()=>Promise.resolve(), setVariable:()=>Promise.resolve(), getVariable:()=>Promise.resolve(), saveStorage:()=>Promise.resolve(), getStorage:()=>Promise.resolve(), deleteStorage:()=>Promise.resolve(), addLoyaltyPoints:()=>Promise.resolve(), getLoyaltyPoints:()=>Promise.resolve() };
|
|
5974
|
-
|
|
6019
|
+
function __seImportShowError(e){document.body.insertAdjacentHTML('beforeend','<pre style="color:#fca5a5;font:11px monospace;padding:8px;">' + (e && e.message ? e.message : e) + '</pre>');}
|
|
6020
|
+
window.addEventListener('error', function(ev){__seImportShowError(ev.error || ev.message);});
|
|
6021
|
+
window.addEventListener('unhandledrejection', function(ev){__seImportShowError(ev.reason);});
|
|
6022
|
+
</script><script>
|
|
6023
|
+
(async function __seImportUserScript(){
|
|
6024
|
+
${safeJs}
|
|
6025
|
+
})().catch(__seImportShowError);
|
|
5975
6026
|
</script></body></html>`;
|
|
5976
6027
|
}, [html, css, js, data]);
|
|
5977
6028
|
return /* @__PURE__ */ jsx14(
|
|
@@ -6529,7 +6580,8 @@ function StepURL({
|
|
|
6529
6580
|
url,
|
|
6530
6581
|
setUrl,
|
|
6531
6582
|
error,
|
|
6532
|
-
busy
|
|
6583
|
+
busy,
|
|
6584
|
+
onSwitchToJwt
|
|
6533
6585
|
}) {
|
|
6534
6586
|
return /* @__PURE__ */ jsxs7("div", { className: panelClass("se-import-panel--narrow"), children: [
|
|
6535
6587
|
/* @__PURE__ */ jsx14(
|
|
@@ -6575,7 +6627,16 @@ function StepURL({
|
|
|
6575
6627
|
] })
|
|
6576
6628
|
] })
|
|
6577
6629
|
] }),
|
|
6578
|
-
error && /* @__PURE__ */ jsx14(ErrorPanel, { message: error.message, hint: error.hint })
|
|
6630
|
+
error && /* @__PURE__ */ jsx14(ErrorPanel, { message: error.message, hint: error.hint }),
|
|
6631
|
+
error?.editorUrl && onSwitchToJwt && /* @__PURE__ */ jsx14("div", { style: { marginTop: 12 }, children: /* @__PURE__ */ jsx14(
|
|
6632
|
+
LSButton,
|
|
6633
|
+
{
|
|
6634
|
+
type: "button",
|
|
6635
|
+
color: "primary",
|
|
6636
|
+
onClick: onSwitchToJwt,
|
|
6637
|
+
label: "Connect with JWT instead"
|
|
6638
|
+
}
|
|
6639
|
+
) })
|
|
6579
6640
|
] });
|
|
6580
6641
|
}
|
|
6581
6642
|
function StepDiscovery({
|
|
@@ -7082,10 +7143,7 @@ function SEImportWizard({
|
|
|
7082
7143
|
const mirrorStartedRef = useRef4(false);
|
|
7083
7144
|
const mirrorAbortRef = useRef4(null);
|
|
7084
7145
|
const filenameCacheRef = useRef4(/* @__PURE__ */ new Map());
|
|
7085
|
-
const assetUrls =
|
|
7086
|
-
() => result ? findSEAssetURLs(result.overlay) : [],
|
|
7087
|
-
[result]
|
|
7088
|
-
);
|
|
7146
|
+
const [assetUrls, setAssetUrls] = useState6([]);
|
|
7089
7147
|
const currentItem = originalReviewItems[reviewIndex];
|
|
7090
7148
|
const currentRow = currentItem ? rowStates[currentItem.moduleId] ?? { state: "pending" } : void 0;
|
|
7091
7149
|
const handleConnect = useCallback3(async () => {
|
|
@@ -7183,6 +7241,7 @@ function SEImportWizard({
|
|
|
7183
7241
|
}
|
|
7184
7242
|
const imported = importSEBootstrap(bootstrap);
|
|
7185
7243
|
setResult(imported);
|
|
7244
|
+
setAssetUrls(findSEAssetURLs(imported.overlay));
|
|
7186
7245
|
setOptions((o) => ({ ...o, name: imported.overlay.name }));
|
|
7187
7246
|
setOriginalReviewItems(imported.reviewItems);
|
|
7188
7247
|
setRowStates(
|
|
@@ -7198,9 +7257,10 @@ function SEImportWizard({
|
|
|
7198
7257
|
mirrorStartedRef.current = false;
|
|
7199
7258
|
setStep("discovery");
|
|
7200
7259
|
} catch (e) {
|
|
7260
|
+
const detail = e?.message ?? String(e);
|
|
7201
7261
|
setLoadError({
|
|
7202
|
-
message: "Could not load the overlay.",
|
|
7203
|
-
hint:
|
|
7262
|
+
message: detail || "Could not load the overlay.",
|
|
7263
|
+
hint: detail ? "If this looks transient, retry in a moment. Otherwise check the URL / token and try again." : void 0
|
|
7204
7264
|
});
|
|
7205
7265
|
} finally {
|
|
7206
7266
|
setLoading(false);
|
|
@@ -7231,7 +7291,8 @@ function SEImportWizard({
|
|
|
7231
7291
|
if (!parts.apikey) {
|
|
7232
7292
|
setLoadError({
|
|
7233
7293
|
message: "That looks like an editor URL, which doesn't include an access token.",
|
|
7234
|
-
hint: `Use the public preview URL (Overlay menu \u2192 Get URL \u2192 Public link). Overlay id: ${parts.overlayId}
|
|
7294
|
+
hint: `Use the public preview URL (Overlay menu \u2192 Get URL \u2192 Public link), or connect your account with a JWT to skip the URL step entirely. Overlay id: ${parts.overlayId}`,
|
|
7295
|
+
editorUrl: true
|
|
7235
7296
|
});
|
|
7236
7297
|
return;
|
|
7237
7298
|
}
|
|
@@ -7240,6 +7301,7 @@ function SEImportWizard({
|
|
|
7240
7301
|
const bootstrap = await fetchSEBootstrap(parts);
|
|
7241
7302
|
const imported = importSEBootstrap(bootstrap);
|
|
7242
7303
|
setResult(imported);
|
|
7304
|
+
setAssetUrls(findSEAssetURLs(imported.overlay));
|
|
7243
7305
|
setOptions((previous) => ({ ...previous, name: imported.overlay.name }));
|
|
7244
7306
|
setOriginalReviewItems(imported.reviewItems);
|
|
7245
7307
|
setRowStates(
|
|
@@ -7253,27 +7315,21 @@ function SEImportWizard({
|
|
|
7253
7315
|
setReviewIndex(0);
|
|
7254
7316
|
setStep("discovery");
|
|
7255
7317
|
} catch (error) {
|
|
7318
|
+
const detail = error?.message ?? String(error);
|
|
7256
7319
|
setLoadError({
|
|
7257
|
-
message: "Could not load the overlay.",
|
|
7258
|
-
hint:
|
|
7320
|
+
message: detail || "Could not load the overlay.",
|
|
7321
|
+
hint: detail ? "If this looks transient, retry in a moment. Otherwise check the URL / token and try again." : void 0
|
|
7259
7322
|
});
|
|
7260
7323
|
} finally {
|
|
7261
7324
|
setLoading(false);
|
|
7262
7325
|
}
|
|
7263
7326
|
}, [url]);
|
|
7327
|
+
const MIRROR_CONCURRENCY = 5;
|
|
7264
7328
|
const processAssetRow = useCallback3(
|
|
7265
7329
|
async (assetUrl, idx, existingByFilename, signal) => {
|
|
7266
7330
|
const filename = filenameFromURL(assetUrl);
|
|
7267
7331
|
const reuseUrl = existingByFilename.get(filename);
|
|
7268
7332
|
if (reuseUrl) {
|
|
7269
|
-
setResult(
|
|
7270
|
-
(current) => current ? {
|
|
7271
|
-
...current,
|
|
7272
|
-
overlay: rewriteAssetURLs(current.overlay, {
|
|
7273
|
-
[assetUrl]: reuseUrl
|
|
7274
|
-
})
|
|
7275
|
-
} : current
|
|
7276
|
-
);
|
|
7277
7333
|
return { state: "reused", newUrl: reuseUrl };
|
|
7278
7334
|
}
|
|
7279
7335
|
if (filename.toLowerCase().endsWith(".svg"))
|
|
@@ -7290,14 +7346,6 @@ function SEImportWizard({
|
|
|
7290
7346
|
signal
|
|
7291
7347
|
);
|
|
7292
7348
|
existingByFilename.set(filename, newUrl);
|
|
7293
|
-
setResult(
|
|
7294
|
-
(current) => current ? {
|
|
7295
|
-
...current,
|
|
7296
|
-
overlay: rewriteAssetURLs(current.overlay, {
|
|
7297
|
-
[assetUrl]: newUrl
|
|
7298
|
-
})
|
|
7299
|
-
} : current
|
|
7300
|
-
);
|
|
7301
7349
|
return { state: "done", newUrl };
|
|
7302
7350
|
} catch (error) {
|
|
7303
7351
|
if (error?.name === "AbortError" || signal?.aborted)
|
|
@@ -7307,8 +7355,15 @@ function SEImportWizard({
|
|
|
7307
7355
|
},
|
|
7308
7356
|
[uploadAsset]
|
|
7309
7357
|
);
|
|
7358
|
+
const applyRewrite = useCallback3((mapping) => {
|
|
7359
|
+
if (Object.keys(mapping).length === 0) return;
|
|
7360
|
+
setResult(
|
|
7361
|
+
(current) => current ? { ...current, overlay: rewriteAssetURLs(current.overlay, mapping) } : current
|
|
7362
|
+
);
|
|
7363
|
+
}, []);
|
|
7310
7364
|
const runMirror = useCallback3(async () => {
|
|
7311
7365
|
if (!result) return;
|
|
7366
|
+
mirrorAbortRef.current?.abort();
|
|
7312
7367
|
mirrorAbortRef.current = new AbortController();
|
|
7313
7368
|
const signal = mirrorAbortRef.current.signal;
|
|
7314
7369
|
setMirrorRunning(true);
|
|
@@ -7323,21 +7378,33 @@ function SEImportWizard({
|
|
|
7323
7378
|
if (name && asset.url) existingByFilename.set(name, asset.url);
|
|
7324
7379
|
}
|
|
7325
7380
|
filenameCacheRef.current = existingByFilename;
|
|
7326
|
-
|
|
7327
|
-
|
|
7328
|
-
|
|
7329
|
-
|
|
7330
|
-
|
|
7331
|
-
|
|
7332
|
-
|
|
7333
|
-
|
|
7334
|
-
|
|
7335
|
-
|
|
7336
|
-
|
|
7337
|
-
|
|
7338
|
-
|
|
7381
|
+
const rewriteMap = {};
|
|
7382
|
+
let cursor = 0;
|
|
7383
|
+
const worker = async () => {
|
|
7384
|
+
while (true) {
|
|
7385
|
+
if (signal.aborted) return;
|
|
7386
|
+
const i = cursor++;
|
|
7387
|
+
if (i >= initialRows.length) return;
|
|
7388
|
+
const outcome = await processAssetRow(
|
|
7389
|
+
initialRows[i].url,
|
|
7390
|
+
i,
|
|
7391
|
+
existingByFilename,
|
|
7392
|
+
signal
|
|
7393
|
+
);
|
|
7394
|
+
if (signal.aborted) return;
|
|
7395
|
+
if (outcome.newUrl && (outcome.state === "done" || outcome.state === "reused")) {
|
|
7396
|
+
rewriteMap[initialRows[i].url] = outcome.newUrl;
|
|
7397
|
+
}
|
|
7398
|
+
setMirrorRows(
|
|
7399
|
+
(previous) => previous.map((row, idx) => idx === i ? { ...row, ...outcome } : row)
|
|
7400
|
+
);
|
|
7401
|
+
}
|
|
7402
|
+
};
|
|
7403
|
+
const poolSize = Math.min(MIRROR_CONCURRENCY, initialRows.length);
|
|
7404
|
+
await Promise.all(Array.from({ length: poolSize }, worker));
|
|
7405
|
+
if (!signal.aborted) applyRewrite(rewriteMap);
|
|
7339
7406
|
setMirrorRunning(false);
|
|
7340
|
-
}, [assetUrls, existingAssets, processAssetRow, result]);
|
|
7407
|
+
}, [applyRewrite, assetUrls, existingAssets, processAssetRow, result]);
|
|
7341
7408
|
const retryMirrorRow = useCallback3(
|
|
7342
7409
|
async (idx) => {
|
|
7343
7410
|
const rowUrl = mirrorRows[idx]?.url;
|
|
@@ -7351,8 +7418,11 @@ function SEImportWizard({
|
|
|
7351
7418
|
setMirrorRows(
|
|
7352
7419
|
(previous) => previous.map((row, i) => i === idx ? { ...row, ...outcome } : row)
|
|
7353
7420
|
);
|
|
7421
|
+
if (outcome.newUrl && (outcome.state === "done" || outcome.state === "reused")) {
|
|
7422
|
+
applyRewrite({ [rowUrl]: outcome.newUrl });
|
|
7423
|
+
}
|
|
7354
7424
|
},
|
|
7355
|
-
[mirrorRows, processAssetRow]
|
|
7425
|
+
[applyRewrite, mirrorRows, processAssetRow]
|
|
7356
7426
|
);
|
|
7357
7427
|
useEffect6(() => {
|
|
7358
7428
|
if (step === "mirror" && !mirrorStartedRef.current) {
|
|
@@ -7360,6 +7430,13 @@ function SEImportWizard({
|
|
|
7360
7430
|
void runMirror();
|
|
7361
7431
|
}
|
|
7362
7432
|
}, [step, runMirror]);
|
|
7433
|
+
useEffect6(() => {
|
|
7434
|
+
return () => {
|
|
7435
|
+
setJwt("");
|
|
7436
|
+
setSeClient(null);
|
|
7437
|
+
mirrorAbortRef.current?.abort();
|
|
7438
|
+
};
|
|
7439
|
+
}, []);
|
|
7363
7440
|
useEffect6(() => {
|
|
7364
7441
|
if (step === "pick" && seClient && overlayList === null && !overlaysLoading) {
|
|
7365
7442
|
void loadOverlayList(seClient);
|
|
@@ -7754,7 +7831,12 @@ function SEImportWizard({
|
|
|
7754
7831
|
url,
|
|
7755
7832
|
setUrl,
|
|
7756
7833
|
error: loadError,
|
|
7757
|
-
busy: loading
|
|
7834
|
+
busy: loading,
|
|
7835
|
+
onSwitchToJwt: () => {
|
|
7836
|
+
setLoadError(null);
|
|
7837
|
+
setEntryMode("jwt");
|
|
7838
|
+
setStep("connect");
|
|
7839
|
+
}
|
|
7758
7840
|
}
|
|
7759
7841
|
),
|
|
7760
7842
|
step === "discovery" && result && /* @__PURE__ */ jsx14(StepDiscovery, { result, assetCount: assetUrls.length }),
|
|
@@ -7867,6 +7949,7 @@ function extractErrorMessage(err) {
|
|
|
7867
7949
|
}
|
|
7868
7950
|
|
|
7869
7951
|
// src/se-import/index.ts
|
|
7952
|
+
var IMPORT_META_KEY = "importMeta";
|
|
7870
7953
|
var REVIEW_REASONS = {
|
|
7871
7954
|
"se-widget-bit-boss": "No native Lumia equivalent \u2014 bit boss fight bar with HP/damage states.",
|
|
7872
7955
|
// `se-widget-hype-cup` intentionally omitted — it now routes to the native
|
|
@@ -7958,145 +8041,148 @@ function importSEBootstrap(bootstrap) {
|
|
|
7958
8041
|
name: seOverlay?.name,
|
|
7959
8042
|
canvas: { width: seOverlay?.settings?.width, height: seOverlay?.settings?.height },
|
|
7960
8043
|
widgetCount: widgets.length,
|
|
7961
|
-
widgetTypes: widgets.map((w) => w.type)
|
|
7962
|
-
fullBootstrap: bootstrap
|
|
8044
|
+
widgetTypes: widgets.map((w) => w.type)
|
|
7963
8045
|
});
|
|
7964
8046
|
const importCanvas = {
|
|
7965
8047
|
width: seOverlay?.settings?.width ?? 1920,
|
|
7966
8048
|
height: seOverlay?.settings?.height ?? 1080
|
|
7967
8049
|
};
|
|
7968
|
-
|
|
8050
|
+
const ctx = { canvas: importCanvas };
|
|
7969
8051
|
const layers = [];
|
|
7970
8052
|
const modules = {};
|
|
7971
8053
|
const reviewItems = [];
|
|
7972
|
-
|
|
7973
|
-
|
|
7974
|
-
|
|
7975
|
-
|
|
7976
|
-
|
|
7977
|
-
|
|
7978
|
-
|
|
7979
|
-
|
|
7980
|
-
|
|
7981
|
-
|
|
7982
|
-
|
|
7983
|
-
|
|
7984
|
-
|
|
7985
|
-
|
|
7986
|
-
|
|
7987
|
-
|
|
7988
|
-
|
|
7989
|
-
|
|
7990
|
-
|
|
7991
|
-
|
|
7992
|
-
|
|
7993
|
-
|
|
7994
|
-
|
|
7995
|
-
|
|
7996
|
-
|
|
7997
|
-
|
|
7998
|
-
|
|
7999
|
-
|
|
8000
|
-
|
|
8001
|
-
|
|
8002
|
-
|
|
8003
|
-
|
|
8004
|
-
|
|
8005
|
-
|
|
8006
|
-
|
|
8007
|
-
|
|
8008
|
-
|
|
8009
|
-
|
|
8010
|
-
|
|
8011
|
-
|
|
8012
|
-
|
|
8013
|
-
|
|
8014
|
-
|
|
8015
|
-
|
|
8016
|
-
|
|
8017
|
-
|
|
8018
|
-
|
|
8019
|
-
|
|
8020
|
-
|
|
8021
|
-
|
|
8022
|
-
|
|
8023
|
-
|
|
8024
|
-
modules[lumiaGroupId] = module;
|
|
8025
|
-
recordCoverage(coverage, { unit: { layer, module }, status: "direct", lumiaType: "group" }, w.type);
|
|
8026
|
-
}
|
|
8027
|
-
for (const w of widgets) {
|
|
8028
|
-
if (w.type === "se-widget-group") continue;
|
|
8029
|
-
const result = dispatch(w);
|
|
8030
|
-
if (typeof w.group === "string") {
|
|
8031
|
-
const parentLumiaGroupId = seUidToLumiaGroupId[w.group];
|
|
8032
|
-
if (parentLumiaGroupId) {
|
|
8033
|
-
result.unit.layer.group = parentLumiaGroupId;
|
|
8054
|
+
const seUidToLumiaGroupId = {};
|
|
8055
|
+
const groupWidgetByUid = {};
|
|
8056
|
+
for (const w of widgets) {
|
|
8057
|
+
if (w.type !== "se-widget-group") continue;
|
|
8058
|
+
const uid = w.variables?.uid;
|
|
8059
|
+
if (typeof uid !== "string") continue;
|
|
8060
|
+
const lumiaGroupId = nanoid3();
|
|
8061
|
+
seUidToLumiaGroupId[uid] = lumiaGroupId;
|
|
8062
|
+
groupWidgetByUid[uid] = w;
|
|
8063
|
+
}
|
|
8064
|
+
for (const uid of Object.keys(groupWidgetByUid)) {
|
|
8065
|
+
const w = groupWidgetByUid[uid];
|
|
8066
|
+
const lumiaGroupId = seUidToLumiaGroupId[uid];
|
|
8067
|
+
const parentUid = typeof w.group === "string" ? w.group : null;
|
|
8068
|
+
const parentLumiaGroupId = parentUid ? seUidToLumiaGroupId[parentUid] ?? null : null;
|
|
8069
|
+
const bounds = seCssToBounds(w.css, { width: 200, height: 200 }, importCanvas);
|
|
8070
|
+
const layer = {
|
|
8071
|
+
id: lumiaGroupId,
|
|
8072
|
+
type: "group",
|
|
8073
|
+
group: parentLumiaGroupId,
|
|
8074
|
+
bounds: {
|
|
8075
|
+
x: bounds.x,
|
|
8076
|
+
y: bounds.y,
|
|
8077
|
+
width: bounds.width,
|
|
8078
|
+
height: bounds.height,
|
|
8079
|
+
opacity: bounds.opacity,
|
|
8080
|
+
zIndex: bounds.zIndex,
|
|
8081
|
+
rotate: 0,
|
|
8082
|
+
scale: [1, 1],
|
|
8083
|
+
matrix: ""
|
|
8084
|
+
},
|
|
8085
|
+
state: {
|
|
8086
|
+
locked: w.locked ?? false,
|
|
8087
|
+
visible: w.visible ?? true,
|
|
8088
|
+
// SE's `variables.expanded` is the editor's collapsed/open state.
|
|
8089
|
+
expanded: w.variables?.expanded ?? true
|
|
8090
|
+
}
|
|
8091
|
+
};
|
|
8092
|
+
const module = {
|
|
8093
|
+
id: lumiaGroupId,
|
|
8094
|
+
loaded: true,
|
|
8095
|
+
settings: { title: defaultLabelForSEWidget(w), type: "group" },
|
|
8096
|
+
lights: [],
|
|
8097
|
+
css: {},
|
|
8098
|
+
content: {
|
|
8099
|
+
[IMPORT_META_KEY]: {
|
|
8100
|
+
source: "streamelements",
|
|
8101
|
+
widgetType: w.type,
|
|
8102
|
+
importedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8103
|
+
widget: w,
|
|
8104
|
+
reason: "Imported as native group",
|
|
8105
|
+
status: "group"
|
|
8034
8106
|
}
|
|
8107
|
+
},
|
|
8108
|
+
events: {},
|
|
8109
|
+
variables: {}
|
|
8110
|
+
};
|
|
8111
|
+
layers.push(layer);
|
|
8112
|
+
modules[lumiaGroupId] = module;
|
|
8113
|
+
recordCoverage(coverage, { unit: { layer, module }, status: "direct", lumiaType: "group" }, w.type);
|
|
8114
|
+
}
|
|
8115
|
+
for (const w of widgets) {
|
|
8116
|
+
if (w.type === "se-widget-group") continue;
|
|
8117
|
+
const result = dispatch(w, ctx);
|
|
8118
|
+
if (typeof w.group === "string") {
|
|
8119
|
+
const parentLumiaGroupId = seUidToLumiaGroupId[w.group];
|
|
8120
|
+
if (parentLumiaGroupId) {
|
|
8121
|
+
result.unit.layer.group = parentLumiaGroupId;
|
|
8035
8122
|
}
|
|
8036
|
-
|
|
8037
|
-
|
|
8038
|
-
|
|
8039
|
-
|
|
8040
|
-
|
|
8041
|
-
|
|
8042
|
-
|
|
8043
|
-
|
|
8044
|
-
|
|
8045
|
-
|
|
8046
|
-
|
|
8047
|
-
};
|
|
8048
|
-
reviewItems.push({
|
|
8049
|
-
moduleId: module.id,
|
|
8050
|
-
seWidget: w,
|
|
8051
|
-
status: result.status,
|
|
8123
|
+
}
|
|
8124
|
+
layers.push(result.unit.layer);
|
|
8125
|
+
const module = result.unit.module;
|
|
8126
|
+
if (result.status === "placeholder" || result.status === "template") {
|
|
8127
|
+
module.content = {
|
|
8128
|
+
...module.content ?? {},
|
|
8129
|
+
[IMPORT_META_KEY]: {
|
|
8130
|
+
source: "streamelements",
|
|
8131
|
+
widgetType: w.type,
|
|
8132
|
+
importedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8133
|
+
widget: w,
|
|
8052
8134
|
reason: reasonFor(w.type, result.status, result.flaggedOff),
|
|
8135
|
+
status: result.status,
|
|
8053
8136
|
flaggedOff: result.flaggedOff
|
|
8054
|
-
}
|
|
8055
|
-
}
|
|
8056
|
-
|
|
8057
|
-
|
|
8058
|
-
|
|
8059
|
-
|
|
8060
|
-
|
|
8061
|
-
|
|
8062
|
-
|
|
8063
|
-
coverage.notes.push("Merch-goal widgets imported as generic goals \u2014 see SE/03-missing-variables.md \xA78 for the missing data source.");
|
|
8064
|
-
}
|
|
8065
|
-
if (coverage.mappings.some((m) => m.seType === "se-widget-custom-event-list")) {
|
|
8066
|
-
coverage.notes.push("\u26A0 Custom widgets are experimental \u2014 they run under the SE compatibility shim (Embed.tsx) which handles most SE APIs but may not cover every edge case. Verify each custom layer renders correctly in the editor.");
|
|
8137
|
+
}
|
|
8138
|
+
};
|
|
8139
|
+
reviewItems.push({
|
|
8140
|
+
moduleId: module.id,
|
|
8141
|
+
seWidget: w,
|
|
8142
|
+
status: result.status,
|
|
8143
|
+
reason: reasonFor(w.type, result.status, result.flaggedOff),
|
|
8144
|
+
flaggedOff: result.flaggedOff
|
|
8145
|
+
});
|
|
8067
8146
|
}
|
|
8068
|
-
|
|
8069
|
-
|
|
8070
|
-
|
|
8071
|
-
|
|
8072
|
-
|
|
8073
|
-
|
|
8074
|
-
|
|
8075
|
-
|
|
8076
|
-
|
|
8077
|
-
|
|
8078
|
-
|
|
8079
|
-
|
|
8080
|
-
|
|
8147
|
+
modules[module.id] = module;
|
|
8148
|
+
recordCoverage(coverage, result, w.type);
|
|
8149
|
+
}
|
|
8150
|
+
if (coverage.mappings.some((m) => m.status === "placeholder")) {
|
|
8151
|
+
coverage.notes.push("Some widgets imported as placeholder text layers \u2014 see SE/05-widget-to-module-mapping.md for status.");
|
|
8152
|
+
}
|
|
8153
|
+
if (coverage.mappings.some((m) => m.seType === "se-widget-merch-goal")) {
|
|
8154
|
+
coverage.notes.push("Merch-goal widgets imported as generic goals \u2014 see SE/03-missing-variables.md \xA78 for the missing data source.");
|
|
8155
|
+
}
|
|
8156
|
+
if (coverage.mappings.some((m) => m.seType === "se-widget-custom-event-list")) {
|
|
8157
|
+
coverage.notes.push("\u26A0 Custom widgets are experimental \u2014 they run under the SE compatibility shim (Embed.tsx) which handles most SE APIs but may not cover every edge case. Verify each custom layer renders correctly in the editor.");
|
|
8158
|
+
}
|
|
8159
|
+
const overlay = {
|
|
8160
|
+
uuid: "",
|
|
8161
|
+
listen_id: "",
|
|
8162
|
+
name: seOverlay?.name ? `[SE] ${seOverlay.name}` : `Imported from StreamElements (${seOverlay?._id ?? ""})`,
|
|
8163
|
+
description: seOverlay?._id ? `Imported from StreamElements overlay ${seOverlay._id}` : "Imported from StreamElements",
|
|
8164
|
+
settings: {
|
|
8081
8165
|
metadata: {
|
|
8082
|
-
width: seOverlay?.settings?.width ??
|
|
8083
|
-
height: seOverlay?.settings?.height ??
|
|
8166
|
+
width: seOverlay?.settings?.width ?? 1920,
|
|
8167
|
+
height: seOverlay?.settings?.height ?? 1080
|
|
8084
8168
|
},
|
|
8085
|
-
|
|
8086
|
-
|
|
8087
|
-
}
|
|
8088
|
-
|
|
8089
|
-
|
|
8090
|
-
|
|
8091
|
-
|
|
8092
|
-
|
|
8093
|
-
|
|
8094
|
-
|
|
8095
|
-
|
|
8096
|
-
|
|
8097
|
-
|
|
8098
|
-
|
|
8099
|
-
|
|
8169
|
+
layers,
|
|
8170
|
+
modules
|
|
8171
|
+
},
|
|
8172
|
+
metadata: {
|
|
8173
|
+
width: seOverlay?.settings?.width ?? null,
|
|
8174
|
+
height: seOverlay?.settings?.height ?? null
|
|
8175
|
+
},
|
|
8176
|
+
layers_count: layers.length,
|
|
8177
|
+
hasFullSettings: true
|
|
8178
|
+
};
|
|
8179
|
+
console.log("[SE-IMPORT] import complete", {
|
|
8180
|
+
overlayName: overlay.name,
|
|
8181
|
+
layerCount: overlay.layers_count,
|
|
8182
|
+
coverage,
|
|
8183
|
+
reviewItemCount: reviewItems.length
|
|
8184
|
+
});
|
|
8185
|
+
return { overlay, coverage, reviewItems };
|
|
8100
8186
|
}
|
|
8101
8187
|
function applyReviewAction(result, moduleId, action, payload) {
|
|
8102
8188
|
if (action === "keep") return result;
|
|
@@ -8124,11 +8210,17 @@ function applyReviewAction(result, moduleId, action, payload) {
|
|
|
8124
8210
|
bounds: existingLayer.bounds,
|
|
8125
8211
|
state: existingLayer.state
|
|
8126
8212
|
};
|
|
8127
|
-
const
|
|
8213
|
+
const importMeta2 = existing2.content?.[IMPORT_META_KEY];
|
|
8214
|
+
const mergedLights = (existing2.lights?.length ?? 0) > 0 ? existing2.lights : transplant.module.lights;
|
|
8215
|
+
const mergedEvents = existing2.events && Object.keys(existing2.events).length > 0 ? { ...transplant.module.events, ...existing2.events } : transplant.module.events;
|
|
8216
|
+
const mergedVariables = existing2.variables && Object.keys(existing2.variables).length > 0 ? { ...transplant.module.variables, ...existing2.variables } : transplant.module.variables;
|
|
8128
8217
|
const newModule = {
|
|
8129
8218
|
...transplant.module,
|
|
8130
8219
|
id: moduleId,
|
|
8131
|
-
|
|
8220
|
+
lights: mergedLights,
|
|
8221
|
+
events: mergedEvents,
|
|
8222
|
+
variables: mergedVariables,
|
|
8223
|
+
content: { ...transplant.module.content ?? {}, [IMPORT_META_KEY]: importMeta2 }
|
|
8132
8224
|
};
|
|
8133
8225
|
const rootOldNewId = transplant.layer.id;
|
|
8134
8226
|
const remappedExtras = (transplant.extras ?? []).map((e) => ({
|
|
@@ -8155,7 +8247,7 @@ function applyReviewAction(result, moduleId, action, payload) {
|
|
|
8155
8247
|
const existing = modules[moduleId];
|
|
8156
8248
|
const generated = payload;
|
|
8157
8249
|
if (!existing || !generated) return result;
|
|
8158
|
-
const
|
|
8250
|
+
const importMeta = existing.content?.[IMPORT_META_KEY];
|
|
8159
8251
|
const replaced = {
|
|
8160
8252
|
...existing,
|
|
8161
8253
|
settings: { ...existing.settings ?? {}, type: "custom" },
|
|
@@ -8169,7 +8261,7 @@ function applyReviewAction(result, moduleId, action, payload) {
|
|
|
8169
8261
|
data: parseLooseJson(generated.data) ?? {},
|
|
8170
8262
|
configs: parseLooseJson(generated.configs) ?? [],
|
|
8171
8263
|
flavor: "ai-generated",
|
|
8172
|
-
|
|
8264
|
+
[IMPORT_META_KEY]: importMeta
|
|
8173
8265
|
}
|
|
8174
8266
|
};
|
|
8175
8267
|
modules[moduleId] = replaced;
|
|
@@ -8198,6 +8290,11 @@ function buildAIPromptForSEWidget(widget, canvas) {
|
|
|
8198
8290
|
const listener = typeof widget.listener === "string" ? widget.listener : null;
|
|
8199
8291
|
const listeners = widget.listeners && typeof widget.listeners === "object" && !Array.isArray(widget.listeners) ? Object.keys(widget.listeners) : null;
|
|
8200
8292
|
const landing = getAILandingBounds(widget, canvas);
|
|
8293
|
+
const VARIABLES_BUDGET = 6e3;
|
|
8294
|
+
const fullVariables = JSON.stringify(variables, null, 2);
|
|
8295
|
+
const truncated = fullVariables.length > VARIABLES_BUDGET;
|
|
8296
|
+
const variablesJson = truncated ? `${fullVariables.slice(0, VARIABLES_BUDGET)}
|
|
8297
|
+
\u2026[truncated \u2014 ${fullVariables.length - VARIABLES_BUDGET} more bytes elided]` : fullVariables;
|
|
8201
8298
|
return [
|
|
8202
8299
|
"Recreate a StreamElements overlay widget as a Lumia Stream custom overlay (HTML + CSS + JS).",
|
|
8203
8300
|
"",
|
|
@@ -8205,10 +8302,11 @@ function buildAIPromptForSEWidget(widget, canvas) {
|
|
|
8205
8302
|
listener ? `Primary event listener: ${listener}` : "",
|
|
8206
8303
|
listeners ? `Subscribes to events: ${listeners.join(", ")}` : "",
|
|
8207
8304
|
`Canvas: ${landing.width} \xD7 ${landing.height}`,
|
|
8305
|
+
truncated ? "Note: the widget config below is truncated \u2014 focus on visible/structural fields and ignore tail content you cannot see." : "",
|
|
8208
8306
|
"",
|
|
8209
8307
|
"Widget configuration (JSON):",
|
|
8210
8308
|
"```json",
|
|
8211
|
-
|
|
8309
|
+
variablesJson,
|
|
8212
8310
|
"```",
|
|
8213
8311
|
"",
|
|
8214
8312
|
"Constraints \u2014 your output must run inside a Lumia Custom Overlay sandboxed iframe:",
|
|
@@ -8244,7 +8342,6 @@ export {
|
|
|
8244
8342
|
SEAuthError,
|
|
8245
8343
|
SEClient,
|
|
8246
8344
|
SEImportWizard,
|
|
8247
|
-
SE_IMPORT_FLAGS,
|
|
8248
8345
|
SE_WIDGET_TO_MARKETPLACE_CANDIDATES,
|
|
8249
8346
|
applyReviewAction,
|
|
8250
8347
|
buildAIPromptForSEWidget,
|