@lumiastream/ui 0.2.8-alpha.16 → 0.2.8-alpha.17
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 +347 -299
- package/dist/se-import.d.ts +6 -2
- package/dist/se-import.js +346 -298
- 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,7 @@ 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);
|
|
3788
3786
|
}
|
|
3789
3787
|
|
|
3790
3788
|
// src/se-import/mappers/goal.ts
|
|
@@ -3822,7 +3820,7 @@ function sourceFromListener(listener) {
|
|
|
3822
3820
|
if (listener.startsWith("merch-")) return "merch_orders";
|
|
3823
3821
|
return "custom";
|
|
3824
3822
|
}
|
|
3825
|
-
function mapGoal(widget) {
|
|
3823
|
+
function mapGoal(widget, ctx) {
|
|
3826
3824
|
const v = widget.variables ?? {};
|
|
3827
3825
|
const listener = v.goalListener ?? (typeof widget.listener === "string" ? widget.listener : null);
|
|
3828
3826
|
const charity = charityGoalVariables(widget.type);
|
|
@@ -3883,7 +3881,7 @@ function mapGoal(widget) {
|
|
|
3883
3881
|
textShadow: seTextCss["text-shadow"] ?? "rgb(0, 0, 0) 1px 1px 1px",
|
|
3884
3882
|
background: "transparent"
|
|
3885
3883
|
}
|
|
3886
|
-
});
|
|
3884
|
+
}, ctx);
|
|
3887
3885
|
}
|
|
3888
3886
|
|
|
3889
3887
|
// src/se-import/mappers/custom.ts
|
|
@@ -3895,19 +3893,46 @@ var SE_FIELD_TYPE_TO_LUMIA = {
|
|
|
3895
3893
|
slider: "slider",
|
|
3896
3894
|
colorpicker: "colorpicker",
|
|
3897
3895
|
fontpicker: "fontpicker",
|
|
3898
|
-
|
|
3896
|
+
// SE's font picker variant — same control surface as `fontpicker` for our
|
|
3897
|
+
// purposes; the SE renderer pulls Google Fonts at runtime which Lumia's
|
|
3898
|
+
// fontpicker also does.
|
|
3899
|
+
googleFont: "fontpicker",
|
|
3900
|
+
// SE asset uploads — the asset URL lands in the `value` as a CDN string,
|
|
3901
|
+
// which our `input` renders fine as-is. Once Lumia ships dedicated asset/
|
|
3902
|
+
// audio pickers wired through the asset library, flip these mappings.
|
|
3903
|
+
image: "input",
|
|
3904
|
+
"image-input": "input",
|
|
3905
|
+
"sound-input": "input",
|
|
3906
|
+
"video-input": "input",
|
|
3907
|
+
// Multi-line text — `input` accepts strings of any length; the editor
|
|
3908
|
+
// surfaces a textarea by inspecting `value.length` heuristically.
|
|
3909
|
+
textarea: "input",
|
|
3910
|
+
hidden: "input",
|
|
3911
|
+
// SE's button is a "fire a JS action" trigger with no persisted value.
|
|
3912
|
+
// Lumia has no equivalent; surface as a non-editable label.
|
|
3913
|
+
button: "input"
|
|
3899
3914
|
};
|
|
3915
|
+
var SE_RESERVED_FIELD_KEYS = /* @__PURE__ */ new Set(["widgetName", "widgetAuthor", "widgetDuration"]);
|
|
3900
3916
|
function translateConfigs(seFields) {
|
|
3901
3917
|
if (!seFields) return [];
|
|
3902
|
-
return Object.entries(seFields).map(([key, f]) =>
|
|
3903
|
-
|
|
3904
|
-
|
|
3905
|
-
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
|
|
3918
|
+
return Object.entries(seFields).filter(([key]) => !SE_RESERVED_FIELD_KEYS.has(key)).map(([key, f]) => {
|
|
3919
|
+
const seType = f.type ?? "text";
|
|
3920
|
+
return {
|
|
3921
|
+
key,
|
|
3922
|
+
label: f.label ?? key,
|
|
3923
|
+
type: SE_FIELD_TYPE_TO_LUMIA[seType] ?? "input",
|
|
3924
|
+
value: f.value,
|
|
3925
|
+
options: f.options,
|
|
3926
|
+
// Preserve the original SE type + group so a future editor UX
|
|
3927
|
+
// upgrade can render the right control (e.g. an actual asset
|
|
3928
|
+
// picker for `image-input`) without re-importing. Mentioned in
|
|
3929
|
+
// docs/SE_IMPORT_GAPS.md under "Other widgets > custom-event-list".
|
|
3930
|
+
seType,
|
|
3931
|
+
...f.group ? { seGroup: f.group } : {}
|
|
3932
|
+
};
|
|
3933
|
+
});
|
|
3909
3934
|
}
|
|
3910
|
-
function mapCustom(widget) {
|
|
3935
|
+
function mapCustom(widget, ctx) {
|
|
3911
3936
|
const v = widget.variables ?? {};
|
|
3912
3937
|
const parse = (x) => {
|
|
3913
3938
|
if (x == null) return void 0;
|
|
@@ -3930,7 +3955,7 @@ function mapCustom(widget) {
|
|
|
3930
3955
|
data: fieldData ?? {},
|
|
3931
3956
|
flavor: "streamelements"
|
|
3932
3957
|
}
|
|
3933
|
-
});
|
|
3958
|
+
}, ctx);
|
|
3934
3959
|
}
|
|
3935
3960
|
|
|
3936
3961
|
// src/se-import/mappers/misc.ts
|
|
@@ -3958,7 +3983,7 @@ function paddingShorthand(message) {
|
|
|
3958
3983
|
const px = (n) => `${n ?? 0}px`;
|
|
3959
3984
|
return `${px(t)} ${px(r)} ${px(b)} ${px(l)}`;
|
|
3960
3985
|
}
|
|
3961
|
-
function mapChatbox(widget) {
|
|
3986
|
+
function mapChatbox(widget, ctx) {
|
|
3962
3987
|
const v = widget.variables ?? {};
|
|
3963
3988
|
const textCss = widget.text?.css ?? {};
|
|
3964
3989
|
const messageCss = textCss.message ?? {};
|
|
@@ -4042,7 +4067,7 @@ function mapChatbox(widget) {
|
|
|
4042
4067
|
se_highlight_users: v.highlight,
|
|
4043
4068
|
se_text_css: textCss
|
|
4044
4069
|
}
|
|
4045
|
-
});
|
|
4070
|
+
}, ctx);
|
|
4046
4071
|
}
|
|
4047
4072
|
var SE_LISTENER_TO_LUMIA_CATEGORY = {
|
|
4048
4073
|
"follower-latest": "follower",
|
|
@@ -4108,7 +4133,7 @@ function computeListenerFilters(seListeners, explicitListener) {
|
|
|
4108
4133
|
if (allowed.size === 0) return [];
|
|
4109
4134
|
return ALL_LUMIA_EVENTLIST_CATEGORIES.filter((c) => !allowed.has(c));
|
|
4110
4135
|
}
|
|
4111
|
-
function mapEventList(widget) {
|
|
4136
|
+
function mapEventList(widget, ctx) {
|
|
4112
4137
|
const v = widget.variables ?? {};
|
|
4113
4138
|
const filters = computeListenerFilters(widget.listeners, widget.listener);
|
|
4114
4139
|
const maxItems = v.visibleEvents ?? v.visibleItems ?? 5;
|
|
@@ -4137,7 +4162,7 @@ function mapEventList(widget) {
|
|
|
4137
4162
|
showDetailedText: false,
|
|
4138
4163
|
hideAlertMessage: false
|
|
4139
4164
|
}
|
|
4140
|
-
});
|
|
4165
|
+
}, ctx);
|
|
4141
4166
|
}
|
|
4142
4167
|
var SE_CREDITS_EVENTS_TO_SHOW = {
|
|
4143
4168
|
mods: false,
|
|
@@ -4277,7 +4302,7 @@ var CREDITS_DEFAULT_TEMPLATES = {
|
|
|
4277
4302
|
topSuperchats: "{{username}} x{{amount}}",
|
|
4278
4303
|
topRedemptions: "{{username}} x{{amount}}"
|
|
4279
4304
|
};
|
|
4280
|
-
function mapCredits(widget) {
|
|
4305
|
+
function mapCredits(widget, ctx) {
|
|
4281
4306
|
const v = widget.variables ?? {};
|
|
4282
4307
|
const itemGap = typeof v.padding === "number" ? v.padding : 8;
|
|
4283
4308
|
const reverseFlow = v.direction === "bottom";
|
|
@@ -4331,7 +4356,7 @@ function mapCredits(widget) {
|
|
|
4331
4356
|
textAlign: "center",
|
|
4332
4357
|
background: "transparent"
|
|
4333
4358
|
}
|
|
4334
|
-
});
|
|
4359
|
+
}, ctx);
|
|
4335
4360
|
}
|
|
4336
4361
|
function inferSlideshowMediaType(mime, url) {
|
|
4337
4362
|
if (typeof mime === "string") {
|
|
@@ -4350,7 +4375,7 @@ function nameFromUrl(url) {
|
|
|
4350
4375
|
return url;
|
|
4351
4376
|
}
|
|
4352
4377
|
}
|
|
4353
|
-
function mapSlideshow(widget) {
|
|
4378
|
+
function mapSlideshow(widget, ctx) {
|
|
4354
4379
|
const v = widget.variables ?? {};
|
|
4355
4380
|
const items = (v.images ?? []).map((raw) => {
|
|
4356
4381
|
const url = typeof raw === "string" ? raw : raw.url ?? raw.src ?? "";
|
|
@@ -4390,21 +4415,13 @@ function mapSlideshow(widget) {
|
|
|
4390
4415
|
se_interval: v.interval,
|
|
4391
4416
|
se_raw_duration: v.duration
|
|
4392
4417
|
}
|
|
4393
|
-
});
|
|
4418
|
+
}, ctx);
|
|
4394
4419
|
}
|
|
4395
|
-
function mapTimer(widget) {
|
|
4420
|
+
function mapTimer(widget, ctx) {
|
|
4396
4421
|
const v = widget.variables ?? {};
|
|
4397
4422
|
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
|
-
};
|
|
4423
|
+
const TIMER_PASSTHROUGH = /* @__PURE__ */ new Set(["days", "hours", "minutes", "seconds"]);
|
|
4424
|
+
const mapTemplate = (template) => translateSeText(template, null, TIMER_PASSTHROUGH);
|
|
4408
4425
|
const absoluteDate = v.date ?? v.endsAt ?? v.endDate;
|
|
4409
4426
|
const target = absoluteDate ? Date.parse(absoluteDate) : NaN;
|
|
4410
4427
|
const hasValidAbsoluteDate = Number.isFinite(target);
|
|
@@ -4480,9 +4497,9 @@ function mapTimer(widget) {
|
|
|
4480
4497
|
se_date_picker: v.datePicker,
|
|
4481
4498
|
se_exact_time: v.exactTime
|
|
4482
4499
|
}
|
|
4483
|
-
});
|
|
4500
|
+
}, ctx);
|
|
4484
4501
|
}
|
|
4485
|
-
function mapNowPlaying(widget) {
|
|
4502
|
+
function mapNowPlaying(widget, ctx) {
|
|
4486
4503
|
const v = widget.variables ?? {};
|
|
4487
4504
|
const source = v.provider === "youtube" ? "youtubemusic" : "spotify";
|
|
4488
4505
|
return buildUnit(widget, "nowplaying", {
|
|
@@ -4512,9 +4529,9 @@ function mapNowPlaying(widget) {
|
|
|
4512
4529
|
textShadow: "rgb(0, 0, 0) 1px 1px 1px",
|
|
4513
4530
|
background: "transparent"
|
|
4514
4531
|
}
|
|
4515
|
-
});
|
|
4532
|
+
}, ctx);
|
|
4516
4533
|
}
|
|
4517
|
-
function mapMediaShare(widget) {
|
|
4534
|
+
function mapMediaShare(widget, ctx) {
|
|
4518
4535
|
const v = widget.variables ?? {};
|
|
4519
4536
|
return buildUnit(widget, "songrequest", {
|
|
4520
4537
|
content: {
|
|
@@ -4555,7 +4572,7 @@ function mapMediaShare(widget) {
|
|
|
4555
4572
|
background: "transparent",
|
|
4556
4573
|
fontSize: 18
|
|
4557
4574
|
}
|
|
4558
|
-
});
|
|
4575
|
+
}, ctx);
|
|
4559
4576
|
}
|
|
4560
4577
|
var SE_KAPPAGEN_EVENT_TO_LUMIA_ALERT = {
|
|
4561
4578
|
follower: "twitch-follower",
|
|
@@ -4578,7 +4595,7 @@ var SE_KAPPAGEN_EVENT_TO_LUMIA_ALERT = {
|
|
|
4578
4595
|
// All three drop silently; the rest of the widget (size, spawn zone,
|
|
4579
4596
|
// per-event flags for the remaining events) still imports.
|
|
4580
4597
|
};
|
|
4581
|
-
function mapKappagen(widget) {
|
|
4598
|
+
function mapKappagen(widget, ctx) {
|
|
4582
4599
|
const v = widget.variables ?? {};
|
|
4583
4600
|
const eventsFromNested = v.eventsConfig ? {
|
|
4584
4601
|
subscriber: v.eventsConfig.subscriber?.enabled ?? true,
|
|
@@ -4630,10 +4647,11 @@ function mapKappagen(widget) {
|
|
|
4630
4647
|
se_direction: v.direction
|
|
4631
4648
|
},
|
|
4632
4649
|
css: { background: "transparent" }
|
|
4633
|
-
}
|
|
4650
|
+
},
|
|
4651
|
+
ctx
|
|
4634
4652
|
);
|
|
4635
4653
|
}
|
|
4636
|
-
function mapLeaderboardFromVariables(widget, currency) {
|
|
4654
|
+
function mapLeaderboardFromVariables(widget, currency, ctx) {
|
|
4637
4655
|
const v = widget.variables ?? {};
|
|
4638
4656
|
const count = v.limit ?? v.visibleItems ?? 10;
|
|
4639
4657
|
const window = v.period === "weekly" ? "week" : v.period === "monthly" ? "month" : "alltime";
|
|
@@ -4656,9 +4674,9 @@ function mapLeaderboardFromVariables(widget, currency) {
|
|
|
4656
4674
|
se_offset: v.offset,
|
|
4657
4675
|
se_layout: v.layout
|
|
4658
4676
|
}
|
|
4659
|
-
});
|
|
4677
|
+
}, ctx);
|
|
4660
4678
|
}
|
|
4661
|
-
function mapGiveaway(widget) {
|
|
4679
|
+
function mapGiveaway(widget, ctx) {
|
|
4662
4680
|
const v = widget.variables ?? {};
|
|
4663
4681
|
const primary = v.colors?.primary ?? "#393853";
|
|
4664
4682
|
const accent = v.colors?.accent ?? "#FF4076";
|
|
@@ -4709,7 +4727,7 @@ function mapGiveaway(widget) {
|
|
|
4709
4727
|
se_display_tickets_purchased: v.displayTicketsPurchased,
|
|
4710
4728
|
se_image_style_width: v.imageStyle?.width
|
|
4711
4729
|
}
|
|
4712
|
-
});
|
|
4730
|
+
}, ctx);
|
|
4713
4731
|
}
|
|
4714
4732
|
var HYPETRAIN_LISTENER_KEYS = /* @__PURE__ */ new Set([
|
|
4715
4733
|
"hypetrain-latest",
|
|
@@ -4740,7 +4758,7 @@ function isHypetrainCustomWidget(widget) {
|
|
|
4740
4758
|
if (enabled.length === 0) return false;
|
|
4741
4759
|
return enabled.every((listener) => HYPETRAIN_LISTENER_KEYS.has(listener));
|
|
4742
4760
|
}
|
|
4743
|
-
function mapHypetrain(widget) {
|
|
4761
|
+
function mapHypetrain(widget, ctx) {
|
|
4744
4762
|
const v = widget.variables ?? {};
|
|
4745
4763
|
return buildUnit(widget, "hypetrain", {
|
|
4746
4764
|
content: {
|
|
@@ -4783,9 +4801,9 @@ function mapHypetrain(widget) {
|
|
|
4783
4801
|
// these via the overlay JSON if they want to recreate a specific look.
|
|
4784
4802
|
se_variables: v
|
|
4785
4803
|
}
|
|
4786
|
-
});
|
|
4804
|
+
}, ctx);
|
|
4787
4805
|
}
|
|
4788
|
-
function mapUnsupportedAsText(widget) {
|
|
4806
|
+
function mapUnsupportedAsText(widget, ctx) {
|
|
4789
4807
|
return buildUnit(widget, "text", {
|
|
4790
4808
|
content: {
|
|
4791
4809
|
value: `[Unsupported SE widget: ${widget.type}]`,
|
|
@@ -4799,7 +4817,7 @@ function mapUnsupportedAsText(widget) {
|
|
|
4799
4817
|
fontWeight: "bold",
|
|
4800
4818
|
background: "rgba(0,0,0,0.4)"
|
|
4801
4819
|
}
|
|
4802
|
-
});
|
|
4820
|
+
}, ctx);
|
|
4803
4821
|
}
|
|
4804
4822
|
|
|
4805
4823
|
// src/se-import/mappers/hypecup.ts
|
|
@@ -4869,7 +4887,7 @@ function categoriesFromListeners(listeners) {
|
|
|
4869
4887
|
if (events.subs) events.resubs = true;
|
|
4870
4888
|
return events;
|
|
4871
4889
|
}
|
|
4872
|
-
function mapHypeCup(widget) {
|
|
4890
|
+
function mapHypeCup(widget, ctx) {
|
|
4873
4891
|
const v = widget.variables ?? {};
|
|
4874
4892
|
const widgetWidth = typeof v.width === "number" && v.width > 0 ? v.width : parsePx(widget.css?.width);
|
|
4875
4893
|
const widgetHeight = typeof v.height === "number" && v.height > 0 ? v.height : parsePx(widget.css?.height);
|
|
@@ -4927,7 +4945,7 @@ function mapHypeCup(widget) {
|
|
|
4927
4945
|
css: {
|
|
4928
4946
|
background: "transparent"
|
|
4929
4947
|
}
|
|
4930
|
-
});
|
|
4948
|
+
}, ctx);
|
|
4931
4949
|
}
|
|
4932
4950
|
function clamp01(n) {
|
|
4933
4951
|
if (!Number.isFinite(n)) return 0.5;
|
|
@@ -4974,7 +4992,7 @@ function listenerIsOn(listeners, key) {
|
|
|
4974
4992
|
if (Array.isArray(listeners)) return listeners.includes(key);
|
|
4975
4993
|
return listeners[key] === true;
|
|
4976
4994
|
}
|
|
4977
|
-
function mapStreamBoss(widget) {
|
|
4995
|
+
function mapStreamBoss(widget, ctx) {
|
|
4978
4996
|
const v = widget.variables ?? {};
|
|
4979
4997
|
const baseHp = typeof v.baseHP === "number" && v.baseHP > 0 ? Math.round(v.baseHP) : 1e3;
|
|
4980
4998
|
const seDamage = v.damage ?? {};
|
|
@@ -5056,7 +5074,7 @@ function mapStreamBoss(widget) {
|
|
|
5056
5074
|
css: {
|
|
5057
5075
|
background: "transparent"
|
|
5058
5076
|
}
|
|
5059
|
-
});
|
|
5077
|
+
}, ctx);
|
|
5060
5078
|
if (theme === "card") {
|
|
5061
5079
|
const MIN_CARD_WIDTH = 480;
|
|
5062
5080
|
const MIN_CARD_HEIGHT = 110;
|
|
@@ -5067,25 +5085,6 @@ function mapStreamBoss(widget) {
|
|
|
5067
5085
|
}
|
|
5068
5086
|
|
|
5069
5087
|
// 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
5088
|
function isWidgetFlaggedOff(_seType) {
|
|
5090
5089
|
return false;
|
|
5091
5090
|
}
|
|
@@ -5140,7 +5139,7 @@ var GOAL_TYPES = /* @__PURE__ */ new Set([
|
|
|
5140
5139
|
"se-widget-merch-goal"
|
|
5141
5140
|
]);
|
|
5142
5141
|
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) {
|
|
5142
|
+
function dispatch(widget, ctx) {
|
|
5144
5143
|
const t = widget.type;
|
|
5145
5144
|
console.log("[SE-IMPORT] dispatch widget", {
|
|
5146
5145
|
seType: t,
|
|
@@ -5151,46 +5150,46 @@ function dispatch(widget) {
|
|
|
5151
5150
|
hasCss: !!widget.css
|
|
5152
5151
|
});
|
|
5153
5152
|
if (isWidgetFlaggedOff(t)) {
|
|
5154
|
-
return { unit: mapUnsupportedAsText(widget), status: "placeholder", lumiaType: "text", flaggedOff: true };
|
|
5153
|
+
return { unit: mapUnsupportedAsText(widget, ctx), status: "placeholder", lumiaType: "text", flaggedOff: true };
|
|
5155
5154
|
}
|
|
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" };
|
|
5155
|
+
if (t === "text") return { unit: mapText(widget, ctx), status: "direct", lumiaType: "text" };
|
|
5156
|
+
if (t === "image") return { unit: mapImage(widget, ctx), status: "direct", lumiaType: "image" };
|
|
5157
|
+
if (t === "video") return { unit: mapVideo(widget, ctx), status: "direct", lumiaType: "video" };
|
|
5159
5158
|
if (t in READOUT_TYPES) {
|
|
5160
|
-
return { unit: mapReadout(widget, READOUT_TYPES[t]), status: "direct", lumiaType: "text" };
|
|
5159
|
+
return { unit: mapReadout(widget, READOUT_TYPES[t], ctx), status: "direct", lumiaType: "text" };
|
|
5161
5160
|
}
|
|
5162
|
-
if (GOAL_TYPES.has(t)) return { unit: mapGoal(widget), status: "direct", lumiaType: "goal" };
|
|
5161
|
+
if (GOAL_TYPES.has(t)) return { unit: mapGoal(widget, ctx), status: "direct", lumiaType: "goal" };
|
|
5163
5162
|
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" };
|
|
5163
|
+
return { unit: mapUnsupportedAsText(widget, ctx), status: "placeholder", lumiaType: "text", flaggedOff: true };
|
|
5164
|
+
}
|
|
5165
|
+
if (t === "se-widget-alert-box") return { unit: mapAlertBox(widget, {}, ctx), status: "direct", lumiaType: "alert" };
|
|
5166
|
+
if (t === "se-widget-alert-box-merch") return { unit: mapAlertBox(widget, { onlyEvents: ["merch"] }, ctx), status: "direct", lumiaType: "alert" };
|
|
5167
|
+
if (t === "se-widget-donor-drive-alert") return { unit: mapSingleAlert(widget, "donordrive-donation", ctx), status: "direct", lumiaType: "alert" };
|
|
5168
|
+
if (t === "se-widget-extralife-alert") return { unit: mapSingleAlert(widget, "extralife-donation", ctx), status: "direct", lumiaType: "alert" };
|
|
5169
|
+
if (t === "se-widget-tiltify-alert") return { unit: mapSingleAlert(widget, "tiltify-campaignDonation", ctx), status: "direct", lumiaType: "alert" };
|
|
5170
|
+
if (EVENTLIST_TYPES.has(t)) return { unit: mapEventList(widget, ctx), status: "direct", lumiaType: "eventlist" };
|
|
5171
|
+
if (t === "se-widget-top-tippers-list") return { unit: mapLeaderboardFromVariables(widget, "tips", ctx), status: "direct", lumiaType: "loyaltyleaderboard" };
|
|
5172
|
+
if (t === "se-widget-top-cheerers-list") return { unit: mapLeaderboardFromVariables(widget, "cheers", ctx), status: "direct", lumiaType: "loyaltyleaderboard" };
|
|
5173
|
+
if (t === "se-widget-top-gifters-list") return { unit: mapLeaderboardFromVariables(widget, "gifts", ctx), status: "direct", lumiaType: "loyaltyleaderboard" };
|
|
5174
|
+
if (t === "se-widget-credit-roll") return { unit: mapCredits(widget, ctx), status: "partial", lumiaType: "credits" };
|
|
5175
|
+
if (t === "se-widget-twitch-chat") return { unit: mapChatbox(widget, ctx), status: "partial", lumiaType: "chatbox" };
|
|
5176
|
+
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" };
|
|
5177
|
+
if (t === "se-widget-countdown") return { unit: mapTimer(widget, ctx), status: "partial", lumiaType: "timer" };
|
|
5178
|
+
if (t === "se-widget-current-song") return { unit: mapNowPlaying(widget, ctx), status: "partial", lumiaType: "nowplaying" };
|
|
5179
|
+
if (t === "se-widget-kappagen") return { unit: mapKappagen(widget, ctx), status: "partial", lumiaType: "emotealert" };
|
|
5180
|
+
if (t === "se-widget-giveaway") return { unit: mapGiveaway(widget, ctx), status: "direct", lumiaType: "raffle" };
|
|
5181
|
+
if (t === "se-widget-media-share") return { unit: mapMediaShare(widget, ctx), status: "partial", lumiaType: "songrequest" };
|
|
5182
|
+
if (t === "se-widget-hype-cup") return { unit: mapHypeCup(widget, ctx), status: "partial", lumiaType: "tipjar" };
|
|
5183
|
+
if (t === "se-widget-bit-boss") return { unit: mapStreamBoss(widget, ctx), status: "partial", lumiaType: "streamboss" };
|
|
5184
|
+
if (t === "se-widget-hype-train") return { unit: mapHypetrain(widget, ctx), status: "partial", lumiaType: "hypetrain" };
|
|
5186
5185
|
if (t === "se-widget-custom-event-list" && isHypetrainCustomWidget(widget)) {
|
|
5187
|
-
return { unit: mapHypetrain(widget), status: "partial", lumiaType: "hypetrain" };
|
|
5186
|
+
return { unit: mapHypetrain(widget, ctx), status: "partial", lumiaType: "hypetrain" };
|
|
5188
5187
|
}
|
|
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" };
|
|
5188
|
+
if (t === "se-widget-custom-event-list") return { unit: mapCustom(widget, ctx), status: "partial", lumiaType: "custom" };
|
|
5189
|
+
if (t === "se-widget-snow") return { unit: mapSnow(widget, ctx), status: "direct", lumiaType: "video" };
|
|
5190
|
+
if (t === "se-widget-halloween-2019") return { unit: mapSlideshow(widget, ctx), status: "direct", lumiaType: "slideshow" };
|
|
5191
|
+
if (SEASONAL_IMAGE_TYPES.has(t)) return { unit: mapImage(widget, ctx), status: "direct", lumiaType: "image" };
|
|
5192
|
+
return { unit: mapUnsupportedAsText(widget, ctx), status: "placeholder", lumiaType: "text" };
|
|
5194
5193
|
}
|
|
5195
5194
|
function recordCoverage(coverage, result, seType) {
|
|
5196
5195
|
const existing = coverage.mappings.find((m) => m.seType === seType);
|
|
@@ -5248,11 +5247,19 @@ function rewriteAssetURLs(overlay, mapping) {
|
|
|
5248
5247
|
settings: JSON.parse(rewritten)
|
|
5249
5248
|
};
|
|
5250
5249
|
}
|
|
5250
|
+
var MAX_MIRROR_ASSET_BYTES = 100 * 1024 * 1024;
|
|
5251
5251
|
async function mirrorOneAsset(url, upload, signal) {
|
|
5252
5252
|
const res = await fetch(url, { mode: "cors", signal });
|
|
5253
5253
|
if (!res.ok) throw new Error(`Source returned HTTP ${res.status}`);
|
|
5254
|
+
const declared = Number(res.headers.get("content-length"));
|
|
5255
|
+
if (Number.isFinite(declared) && declared > MAX_MIRROR_ASSET_BYTES) {
|
|
5256
|
+
throw new Error(`Asset is ${(declared / (1024 * 1024)).toFixed(1)} MB \u2014 over the ${MAX_MIRROR_ASSET_BYTES / (1024 * 1024)} MB import cap.`);
|
|
5257
|
+
}
|
|
5254
5258
|
const blob = await res.blob();
|
|
5255
5259
|
if (signal?.aborted) throw new DOMException("Aborted", "AbortError");
|
|
5260
|
+
if (blob.size > MAX_MIRROR_ASSET_BYTES) {
|
|
5261
|
+
throw new Error(`Asset is ${(blob.size / (1024 * 1024)).toFixed(1)} MB \u2014 over the ${MAX_MIRROR_ASSET_BYTES / (1024 * 1024)} MB import cap.`);
|
|
5262
|
+
}
|
|
5256
5263
|
const filename = filenameFromURL(url);
|
|
5257
5264
|
const type = blob.type || guessMime(filename);
|
|
5258
5265
|
const file = new File([blob], filename, { type });
|
|
@@ -5965,13 +5972,19 @@ function CustomOverlayPreview({
|
|
|
5965
5972
|
}) {
|
|
5966
5973
|
const srcdoc = useMemo5(() => {
|
|
5967
5974
|
const safeData = JSON.stringify(data ?? {});
|
|
5968
|
-
const safeJs =
|
|
5975
|
+
const safeJs = (js || "").replace(/<\/script>/gi, "<\\/script>");
|
|
5969
5976
|
const safeHtml = (html || "").replace(/<\/script>/gi, "<\\/script>");
|
|
5970
5977
|
const safeCss = (css || "").replace(/<\/style>/gi, "<\\/style>");
|
|
5971
5978
|
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
5979
|
window.DATA = ${safeData};
|
|
5973
5980
|
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
|
-
|
|
5981
|
+
function __seImportShowError(e){document.body.insertAdjacentHTML('beforeend','<pre style="color:#fca5a5;font:11px monospace;padding:8px;">' + (e && e.message ? e.message : e) + '</pre>');}
|
|
5982
|
+
window.addEventListener('error', function(ev){__seImportShowError(ev.error || ev.message);});
|
|
5983
|
+
window.addEventListener('unhandledrejection', function(ev){__seImportShowError(ev.reason);});
|
|
5984
|
+
</script><script>
|
|
5985
|
+
(async function __seImportUserScript(){
|
|
5986
|
+
${safeJs}
|
|
5987
|
+
})().catch(__seImportShowError);
|
|
5975
5988
|
</script></body></html>`;
|
|
5976
5989
|
}, [html, css, js, data]);
|
|
5977
5990
|
return /* @__PURE__ */ jsx14(
|
|
@@ -6529,7 +6542,8 @@ function StepURL({
|
|
|
6529
6542
|
url,
|
|
6530
6543
|
setUrl,
|
|
6531
6544
|
error,
|
|
6532
|
-
busy
|
|
6545
|
+
busy,
|
|
6546
|
+
onSwitchToJwt
|
|
6533
6547
|
}) {
|
|
6534
6548
|
return /* @__PURE__ */ jsxs7("div", { className: panelClass("se-import-panel--narrow"), children: [
|
|
6535
6549
|
/* @__PURE__ */ jsx14(
|
|
@@ -6575,7 +6589,16 @@ function StepURL({
|
|
|
6575
6589
|
] })
|
|
6576
6590
|
] })
|
|
6577
6591
|
] }),
|
|
6578
|
-
error && /* @__PURE__ */ jsx14(ErrorPanel, { message: error.message, hint: error.hint })
|
|
6592
|
+
error && /* @__PURE__ */ jsx14(ErrorPanel, { message: error.message, hint: error.hint }),
|
|
6593
|
+
error?.editorUrl && onSwitchToJwt && /* @__PURE__ */ jsx14("div", { style: { marginTop: 12 }, children: /* @__PURE__ */ jsx14(
|
|
6594
|
+
LSButton,
|
|
6595
|
+
{
|
|
6596
|
+
type: "button",
|
|
6597
|
+
color: "primary",
|
|
6598
|
+
onClick: onSwitchToJwt,
|
|
6599
|
+
label: "Connect with JWT instead"
|
|
6600
|
+
}
|
|
6601
|
+
) })
|
|
6579
6602
|
] });
|
|
6580
6603
|
}
|
|
6581
6604
|
function StepDiscovery({
|
|
@@ -7082,10 +7105,7 @@ function SEImportWizard({
|
|
|
7082
7105
|
const mirrorStartedRef = useRef4(false);
|
|
7083
7106
|
const mirrorAbortRef = useRef4(null);
|
|
7084
7107
|
const filenameCacheRef = useRef4(/* @__PURE__ */ new Map());
|
|
7085
|
-
const assetUrls =
|
|
7086
|
-
() => result ? findSEAssetURLs(result.overlay) : [],
|
|
7087
|
-
[result]
|
|
7088
|
-
);
|
|
7108
|
+
const [assetUrls, setAssetUrls] = useState6([]);
|
|
7089
7109
|
const currentItem = originalReviewItems[reviewIndex];
|
|
7090
7110
|
const currentRow = currentItem ? rowStates[currentItem.moduleId] ?? { state: "pending" } : void 0;
|
|
7091
7111
|
const handleConnect = useCallback3(async () => {
|
|
@@ -7183,6 +7203,7 @@ function SEImportWizard({
|
|
|
7183
7203
|
}
|
|
7184
7204
|
const imported = importSEBootstrap(bootstrap);
|
|
7185
7205
|
setResult(imported);
|
|
7206
|
+
setAssetUrls(findSEAssetURLs(imported.overlay));
|
|
7186
7207
|
setOptions((o) => ({ ...o, name: imported.overlay.name }));
|
|
7187
7208
|
setOriginalReviewItems(imported.reviewItems);
|
|
7188
7209
|
setRowStates(
|
|
@@ -7198,9 +7219,10 @@ function SEImportWizard({
|
|
|
7198
7219
|
mirrorStartedRef.current = false;
|
|
7199
7220
|
setStep("discovery");
|
|
7200
7221
|
} catch (e) {
|
|
7222
|
+
const detail = e?.message ?? String(e);
|
|
7201
7223
|
setLoadError({
|
|
7202
|
-
message: "Could not load the overlay.",
|
|
7203
|
-
hint:
|
|
7224
|
+
message: detail || "Could not load the overlay.",
|
|
7225
|
+
hint: detail ? "If this looks transient, retry in a moment. Otherwise check the URL / token and try again." : void 0
|
|
7204
7226
|
});
|
|
7205
7227
|
} finally {
|
|
7206
7228
|
setLoading(false);
|
|
@@ -7231,7 +7253,8 @@ function SEImportWizard({
|
|
|
7231
7253
|
if (!parts.apikey) {
|
|
7232
7254
|
setLoadError({
|
|
7233
7255
|
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}
|
|
7256
|
+
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}`,
|
|
7257
|
+
editorUrl: true
|
|
7235
7258
|
});
|
|
7236
7259
|
return;
|
|
7237
7260
|
}
|
|
@@ -7240,6 +7263,7 @@ function SEImportWizard({
|
|
|
7240
7263
|
const bootstrap = await fetchSEBootstrap(parts);
|
|
7241
7264
|
const imported = importSEBootstrap(bootstrap);
|
|
7242
7265
|
setResult(imported);
|
|
7266
|
+
setAssetUrls(findSEAssetURLs(imported.overlay));
|
|
7243
7267
|
setOptions((previous) => ({ ...previous, name: imported.overlay.name }));
|
|
7244
7268
|
setOriginalReviewItems(imported.reviewItems);
|
|
7245
7269
|
setRowStates(
|
|
@@ -7253,27 +7277,21 @@ function SEImportWizard({
|
|
|
7253
7277
|
setReviewIndex(0);
|
|
7254
7278
|
setStep("discovery");
|
|
7255
7279
|
} catch (error) {
|
|
7280
|
+
const detail = error?.message ?? String(error);
|
|
7256
7281
|
setLoadError({
|
|
7257
|
-
message: "Could not load the overlay.",
|
|
7258
|
-
hint:
|
|
7282
|
+
message: detail || "Could not load the overlay.",
|
|
7283
|
+
hint: detail ? "If this looks transient, retry in a moment. Otherwise check the URL / token and try again." : void 0
|
|
7259
7284
|
});
|
|
7260
7285
|
} finally {
|
|
7261
7286
|
setLoading(false);
|
|
7262
7287
|
}
|
|
7263
7288
|
}, [url]);
|
|
7289
|
+
const MIRROR_CONCURRENCY = 5;
|
|
7264
7290
|
const processAssetRow = useCallback3(
|
|
7265
7291
|
async (assetUrl, idx, existingByFilename, signal) => {
|
|
7266
7292
|
const filename = filenameFromURL(assetUrl);
|
|
7267
7293
|
const reuseUrl = existingByFilename.get(filename);
|
|
7268
7294
|
if (reuseUrl) {
|
|
7269
|
-
setResult(
|
|
7270
|
-
(current) => current ? {
|
|
7271
|
-
...current,
|
|
7272
|
-
overlay: rewriteAssetURLs(current.overlay, {
|
|
7273
|
-
[assetUrl]: reuseUrl
|
|
7274
|
-
})
|
|
7275
|
-
} : current
|
|
7276
|
-
);
|
|
7277
7295
|
return { state: "reused", newUrl: reuseUrl };
|
|
7278
7296
|
}
|
|
7279
7297
|
if (filename.toLowerCase().endsWith(".svg"))
|
|
@@ -7290,14 +7308,6 @@ function SEImportWizard({
|
|
|
7290
7308
|
signal
|
|
7291
7309
|
);
|
|
7292
7310
|
existingByFilename.set(filename, newUrl);
|
|
7293
|
-
setResult(
|
|
7294
|
-
(current) => current ? {
|
|
7295
|
-
...current,
|
|
7296
|
-
overlay: rewriteAssetURLs(current.overlay, {
|
|
7297
|
-
[assetUrl]: newUrl
|
|
7298
|
-
})
|
|
7299
|
-
} : current
|
|
7300
|
-
);
|
|
7301
7311
|
return { state: "done", newUrl };
|
|
7302
7312
|
} catch (error) {
|
|
7303
7313
|
if (error?.name === "AbortError" || signal?.aborted)
|
|
@@ -7307,8 +7317,15 @@ function SEImportWizard({
|
|
|
7307
7317
|
},
|
|
7308
7318
|
[uploadAsset]
|
|
7309
7319
|
);
|
|
7320
|
+
const applyRewrite = useCallback3((mapping) => {
|
|
7321
|
+
if (Object.keys(mapping).length === 0) return;
|
|
7322
|
+
setResult(
|
|
7323
|
+
(current) => current ? { ...current, overlay: rewriteAssetURLs(current.overlay, mapping) } : current
|
|
7324
|
+
);
|
|
7325
|
+
}, []);
|
|
7310
7326
|
const runMirror = useCallback3(async () => {
|
|
7311
7327
|
if (!result) return;
|
|
7328
|
+
mirrorAbortRef.current?.abort();
|
|
7312
7329
|
mirrorAbortRef.current = new AbortController();
|
|
7313
7330
|
const signal = mirrorAbortRef.current.signal;
|
|
7314
7331
|
setMirrorRunning(true);
|
|
@@ -7323,21 +7340,33 @@ function SEImportWizard({
|
|
|
7323
7340
|
if (name && asset.url) existingByFilename.set(name, asset.url);
|
|
7324
7341
|
}
|
|
7325
7342
|
filenameCacheRef.current = existingByFilename;
|
|
7326
|
-
|
|
7327
|
-
|
|
7328
|
-
|
|
7329
|
-
|
|
7330
|
-
|
|
7331
|
-
|
|
7332
|
-
|
|
7333
|
-
|
|
7334
|
-
|
|
7335
|
-
|
|
7336
|
-
|
|
7337
|
-
|
|
7338
|
-
|
|
7343
|
+
const rewriteMap = {};
|
|
7344
|
+
let cursor = 0;
|
|
7345
|
+
const worker = async () => {
|
|
7346
|
+
while (true) {
|
|
7347
|
+
if (signal.aborted) return;
|
|
7348
|
+
const i = cursor++;
|
|
7349
|
+
if (i >= initialRows.length) return;
|
|
7350
|
+
const outcome = await processAssetRow(
|
|
7351
|
+
initialRows[i].url,
|
|
7352
|
+
i,
|
|
7353
|
+
existingByFilename,
|
|
7354
|
+
signal
|
|
7355
|
+
);
|
|
7356
|
+
if (signal.aborted) return;
|
|
7357
|
+
if (outcome.newUrl && (outcome.state === "done" || outcome.state === "reused")) {
|
|
7358
|
+
rewriteMap[initialRows[i].url] = outcome.newUrl;
|
|
7359
|
+
}
|
|
7360
|
+
setMirrorRows(
|
|
7361
|
+
(previous) => previous.map((row, idx) => idx === i ? { ...row, ...outcome } : row)
|
|
7362
|
+
);
|
|
7363
|
+
}
|
|
7364
|
+
};
|
|
7365
|
+
const poolSize = Math.min(MIRROR_CONCURRENCY, initialRows.length);
|
|
7366
|
+
await Promise.all(Array.from({ length: poolSize }, worker));
|
|
7367
|
+
if (!signal.aborted) applyRewrite(rewriteMap);
|
|
7339
7368
|
setMirrorRunning(false);
|
|
7340
|
-
}, [assetUrls, existingAssets, processAssetRow, result]);
|
|
7369
|
+
}, [applyRewrite, assetUrls, existingAssets, processAssetRow, result]);
|
|
7341
7370
|
const retryMirrorRow = useCallback3(
|
|
7342
7371
|
async (idx) => {
|
|
7343
7372
|
const rowUrl = mirrorRows[idx]?.url;
|
|
@@ -7351,8 +7380,11 @@ function SEImportWizard({
|
|
|
7351
7380
|
setMirrorRows(
|
|
7352
7381
|
(previous) => previous.map((row, i) => i === idx ? { ...row, ...outcome } : row)
|
|
7353
7382
|
);
|
|
7383
|
+
if (outcome.newUrl && (outcome.state === "done" || outcome.state === "reused")) {
|
|
7384
|
+
applyRewrite({ [rowUrl]: outcome.newUrl });
|
|
7385
|
+
}
|
|
7354
7386
|
},
|
|
7355
|
-
[mirrorRows, processAssetRow]
|
|
7387
|
+
[applyRewrite, mirrorRows, processAssetRow]
|
|
7356
7388
|
);
|
|
7357
7389
|
useEffect6(() => {
|
|
7358
7390
|
if (step === "mirror" && !mirrorStartedRef.current) {
|
|
@@ -7360,6 +7392,13 @@ function SEImportWizard({
|
|
|
7360
7392
|
void runMirror();
|
|
7361
7393
|
}
|
|
7362
7394
|
}, [step, runMirror]);
|
|
7395
|
+
useEffect6(() => {
|
|
7396
|
+
return () => {
|
|
7397
|
+
setJwt("");
|
|
7398
|
+
setSeClient(null);
|
|
7399
|
+
mirrorAbortRef.current?.abort();
|
|
7400
|
+
};
|
|
7401
|
+
}, []);
|
|
7363
7402
|
useEffect6(() => {
|
|
7364
7403
|
if (step === "pick" && seClient && overlayList === null && !overlaysLoading) {
|
|
7365
7404
|
void loadOverlayList(seClient);
|
|
@@ -7754,7 +7793,12 @@ function SEImportWizard({
|
|
|
7754
7793
|
url,
|
|
7755
7794
|
setUrl,
|
|
7756
7795
|
error: loadError,
|
|
7757
|
-
busy: loading
|
|
7796
|
+
busy: loading,
|
|
7797
|
+
onSwitchToJwt: () => {
|
|
7798
|
+
setLoadError(null);
|
|
7799
|
+
setEntryMode("jwt");
|
|
7800
|
+
setStep("connect");
|
|
7801
|
+
}
|
|
7758
7802
|
}
|
|
7759
7803
|
),
|
|
7760
7804
|
step === "discovery" && result && /* @__PURE__ */ jsx14(StepDiscovery, { result, assetCount: assetUrls.length }),
|
|
@@ -7958,145 +8002,138 @@ function importSEBootstrap(bootstrap) {
|
|
|
7958
8002
|
name: seOverlay?.name,
|
|
7959
8003
|
canvas: { width: seOverlay?.settings?.width, height: seOverlay?.settings?.height },
|
|
7960
8004
|
widgetCount: widgets.length,
|
|
7961
|
-
widgetTypes: widgets.map((w) => w.type)
|
|
7962
|
-
fullBootstrap: bootstrap
|
|
8005
|
+
widgetTypes: widgets.map((w) => w.type)
|
|
7963
8006
|
});
|
|
7964
8007
|
const importCanvas = {
|
|
7965
8008
|
width: seOverlay?.settings?.width ?? 1920,
|
|
7966
8009
|
height: seOverlay?.settings?.height ?? 1080
|
|
7967
8010
|
};
|
|
7968
|
-
|
|
8011
|
+
const ctx = { canvas: importCanvas };
|
|
7969
8012
|
const layers = [];
|
|
7970
8013
|
const modules = {};
|
|
7971
8014
|
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
|
-
expanded: w.variables?.expanded ?? true
|
|
8009
|
-
}
|
|
8010
|
-
};
|
|
8011
|
-
const module = {
|
|
8012
|
-
id: lumiaGroupId,
|
|
8013
|
-
loaded: true,
|
|
8014
|
-
settings: { title: defaultLabelForSEWidget(w), type: "group" },
|
|
8015
|
-
lights: [],
|
|
8016
|
-
css: {},
|
|
8017
|
-
content: {
|
|
8018
|
-
se_provenance: { widget: w, reason: "Imported as native group", status: "group" }
|
|
8019
|
-
},
|
|
8020
|
-
events: {},
|
|
8021
|
-
variables: {}
|
|
8022
|
-
};
|
|
8023
|
-
layers.push(layer);
|
|
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;
|
|
8034
|
-
}
|
|
8015
|
+
const seUidToLumiaGroupId = {};
|
|
8016
|
+
const groupWidgetByUid = {};
|
|
8017
|
+
for (const w of widgets) {
|
|
8018
|
+
if (w.type !== "se-widget-group") continue;
|
|
8019
|
+
const uid = w.variables?.uid;
|
|
8020
|
+
if (typeof uid !== "string") continue;
|
|
8021
|
+
const lumiaGroupId = nanoid3();
|
|
8022
|
+
seUidToLumiaGroupId[uid] = lumiaGroupId;
|
|
8023
|
+
groupWidgetByUid[uid] = w;
|
|
8024
|
+
}
|
|
8025
|
+
for (const uid of Object.keys(groupWidgetByUid)) {
|
|
8026
|
+
const w = groupWidgetByUid[uid];
|
|
8027
|
+
const lumiaGroupId = seUidToLumiaGroupId[uid];
|
|
8028
|
+
const parentUid = typeof w.group === "string" ? w.group : null;
|
|
8029
|
+
const parentLumiaGroupId = parentUid ? seUidToLumiaGroupId[parentUid] ?? null : null;
|
|
8030
|
+
const bounds = seCssToBounds(w.css, { width: 200, height: 200 }, importCanvas);
|
|
8031
|
+
const layer = {
|
|
8032
|
+
id: lumiaGroupId,
|
|
8033
|
+
type: "group",
|
|
8034
|
+
group: parentLumiaGroupId,
|
|
8035
|
+
bounds: {
|
|
8036
|
+
x: bounds.x,
|
|
8037
|
+
y: bounds.y,
|
|
8038
|
+
width: bounds.width,
|
|
8039
|
+
height: bounds.height,
|
|
8040
|
+
opacity: bounds.opacity,
|
|
8041
|
+
zIndex: bounds.zIndex,
|
|
8042
|
+
rotate: 0,
|
|
8043
|
+
scale: [1, 1],
|
|
8044
|
+
matrix: ""
|
|
8045
|
+
},
|
|
8046
|
+
state: {
|
|
8047
|
+
locked: w.locked ?? false,
|
|
8048
|
+
visible: w.visible ?? true,
|
|
8049
|
+
// SE's `variables.expanded` is the editor's collapsed/open state.
|
|
8050
|
+
expanded: w.variables?.expanded ?? true
|
|
8035
8051
|
}
|
|
8036
|
-
|
|
8037
|
-
|
|
8038
|
-
|
|
8039
|
-
|
|
8040
|
-
|
|
8041
|
-
|
|
8042
|
-
|
|
8043
|
-
|
|
8044
|
-
|
|
8045
|
-
|
|
8046
|
-
|
|
8047
|
-
|
|
8048
|
-
|
|
8049
|
-
|
|
8050
|
-
|
|
8051
|
-
|
|
8052
|
-
|
|
8053
|
-
|
|
8054
|
-
|
|
8052
|
+
};
|
|
8053
|
+
const module = {
|
|
8054
|
+
id: lumiaGroupId,
|
|
8055
|
+
loaded: true,
|
|
8056
|
+
settings: { title: defaultLabelForSEWidget(w), type: "group" },
|
|
8057
|
+
lights: [],
|
|
8058
|
+
css: {},
|
|
8059
|
+
content: {
|
|
8060
|
+
se_provenance: { widget: w, reason: "Imported as native group", status: "group" }
|
|
8061
|
+
},
|
|
8062
|
+
events: {},
|
|
8063
|
+
variables: {}
|
|
8064
|
+
};
|
|
8065
|
+
layers.push(layer);
|
|
8066
|
+
modules[lumiaGroupId] = module;
|
|
8067
|
+
recordCoverage(coverage, { unit: { layer, module }, status: "direct", lumiaType: "group" }, w.type);
|
|
8068
|
+
}
|
|
8069
|
+
for (const w of widgets) {
|
|
8070
|
+
if (w.type === "se-widget-group") continue;
|
|
8071
|
+
const result = dispatch(w, ctx);
|
|
8072
|
+
if (typeof w.group === "string") {
|
|
8073
|
+
const parentLumiaGroupId = seUidToLumiaGroupId[w.group];
|
|
8074
|
+
if (parentLumiaGroupId) {
|
|
8075
|
+
result.unit.layer.group = parentLumiaGroupId;
|
|
8055
8076
|
}
|
|
8056
|
-
modules[module.id] = module;
|
|
8057
|
-
recordCoverage(coverage, result, w.type);
|
|
8058
|
-
}
|
|
8059
|
-
if (coverage.mappings.some((m) => m.status === "placeholder")) {
|
|
8060
|
-
coverage.notes.push("Some widgets imported as placeholder text layers \u2014 see SE/05-widget-to-module-mapping.md for status.");
|
|
8061
8077
|
}
|
|
8062
|
-
|
|
8063
|
-
|
|
8064
|
-
|
|
8065
|
-
|
|
8066
|
-
|
|
8078
|
+
layers.push(result.unit.layer);
|
|
8079
|
+
const module = result.unit.module;
|
|
8080
|
+
if (result.status === "placeholder" || result.status === "template") {
|
|
8081
|
+
module.content = {
|
|
8082
|
+
...module.content ?? {},
|
|
8083
|
+
se_provenance: {
|
|
8084
|
+
widget: w,
|
|
8085
|
+
reason: reasonFor(w.type, result.status, result.flaggedOff),
|
|
8086
|
+
status: result.status,
|
|
8087
|
+
flaggedOff: result.flaggedOff
|
|
8088
|
+
}
|
|
8089
|
+
};
|
|
8090
|
+
reviewItems.push({
|
|
8091
|
+
moduleId: module.id,
|
|
8092
|
+
seWidget: w,
|
|
8093
|
+
status: result.status,
|
|
8094
|
+
reason: reasonFor(w.type, result.status, result.flaggedOff),
|
|
8095
|
+
flaggedOff: result.flaggedOff
|
|
8096
|
+
});
|
|
8067
8097
|
}
|
|
8068
|
-
|
|
8069
|
-
|
|
8070
|
-
|
|
8071
|
-
|
|
8072
|
-
|
|
8073
|
-
|
|
8074
|
-
|
|
8075
|
-
|
|
8076
|
-
|
|
8077
|
-
|
|
8078
|
-
|
|
8079
|
-
|
|
8080
|
-
|
|
8098
|
+
modules[module.id] = module;
|
|
8099
|
+
recordCoverage(coverage, result, w.type);
|
|
8100
|
+
}
|
|
8101
|
+
if (coverage.mappings.some((m) => m.status === "placeholder")) {
|
|
8102
|
+
coverage.notes.push("Some widgets imported as placeholder text layers \u2014 see SE/05-widget-to-module-mapping.md for status.");
|
|
8103
|
+
}
|
|
8104
|
+
if (coverage.mappings.some((m) => m.seType === "se-widget-merch-goal")) {
|
|
8105
|
+
coverage.notes.push("Merch-goal widgets imported as generic goals \u2014 see SE/03-missing-variables.md \xA78 for the missing data source.");
|
|
8106
|
+
}
|
|
8107
|
+
if (coverage.mappings.some((m) => m.seType === "se-widget-custom-event-list")) {
|
|
8108
|
+
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.");
|
|
8109
|
+
}
|
|
8110
|
+
const overlay = {
|
|
8111
|
+
uuid: "",
|
|
8112
|
+
listen_id: "",
|
|
8113
|
+
name: seOverlay?.name ? `[SE] ${seOverlay.name}` : `Imported from StreamElements (${seOverlay?._id ?? ""})`,
|
|
8114
|
+
description: seOverlay?._id ? `Imported from StreamElements overlay ${seOverlay._id}` : "Imported from StreamElements",
|
|
8115
|
+
settings: {
|
|
8081
8116
|
metadata: {
|
|
8082
|
-
width: seOverlay?.settings?.width ??
|
|
8083
|
-
height: seOverlay?.settings?.height ??
|
|
8117
|
+
width: seOverlay?.settings?.width ?? 1920,
|
|
8118
|
+
height: seOverlay?.settings?.height ?? 1080
|
|
8084
8119
|
},
|
|
8085
|
-
|
|
8086
|
-
|
|
8087
|
-
}
|
|
8088
|
-
|
|
8089
|
-
|
|
8090
|
-
|
|
8091
|
-
|
|
8092
|
-
|
|
8093
|
-
|
|
8094
|
-
|
|
8095
|
-
|
|
8096
|
-
|
|
8097
|
-
|
|
8098
|
-
|
|
8099
|
-
|
|
8120
|
+
layers,
|
|
8121
|
+
modules
|
|
8122
|
+
},
|
|
8123
|
+
metadata: {
|
|
8124
|
+
width: seOverlay?.settings?.width ?? null,
|
|
8125
|
+
height: seOverlay?.settings?.height ?? null
|
|
8126
|
+
},
|
|
8127
|
+
layers_count: layers.length,
|
|
8128
|
+
hasFullSettings: true
|
|
8129
|
+
};
|
|
8130
|
+
console.log("[SE-IMPORT] import complete", {
|
|
8131
|
+
overlayName: overlay.name,
|
|
8132
|
+
layerCount: overlay.layers_count,
|
|
8133
|
+
coverage,
|
|
8134
|
+
reviewItemCount: reviewItems.length
|
|
8135
|
+
});
|
|
8136
|
+
return { overlay, coverage, reviewItems };
|
|
8100
8137
|
}
|
|
8101
8138
|
function applyReviewAction(result, moduleId, action, payload) {
|
|
8102
8139
|
if (action === "keep") return result;
|
|
@@ -8125,9 +8162,15 @@ function applyReviewAction(result, moduleId, action, payload) {
|
|
|
8125
8162
|
state: existingLayer.state
|
|
8126
8163
|
};
|
|
8127
8164
|
const provenance2 = existing2.content?.se_provenance;
|
|
8165
|
+
const mergedLights = (existing2.lights?.length ?? 0) > 0 ? existing2.lights : transplant.module.lights;
|
|
8166
|
+
const mergedEvents = existing2.events && Object.keys(existing2.events).length > 0 ? { ...transplant.module.events, ...existing2.events } : transplant.module.events;
|
|
8167
|
+
const mergedVariables = existing2.variables && Object.keys(existing2.variables).length > 0 ? { ...transplant.module.variables, ...existing2.variables } : transplant.module.variables;
|
|
8128
8168
|
const newModule = {
|
|
8129
8169
|
...transplant.module,
|
|
8130
8170
|
id: moduleId,
|
|
8171
|
+
lights: mergedLights,
|
|
8172
|
+
events: mergedEvents,
|
|
8173
|
+
variables: mergedVariables,
|
|
8131
8174
|
content: { ...transplant.module.content ?? {}, se_provenance: provenance2 }
|
|
8132
8175
|
};
|
|
8133
8176
|
const rootOldNewId = transplant.layer.id;
|
|
@@ -8198,6 +8241,11 @@ function buildAIPromptForSEWidget(widget, canvas) {
|
|
|
8198
8241
|
const listener = typeof widget.listener === "string" ? widget.listener : null;
|
|
8199
8242
|
const listeners = widget.listeners && typeof widget.listeners === "object" && !Array.isArray(widget.listeners) ? Object.keys(widget.listeners) : null;
|
|
8200
8243
|
const landing = getAILandingBounds(widget, canvas);
|
|
8244
|
+
const VARIABLES_BUDGET = 6e3;
|
|
8245
|
+
const fullVariables = JSON.stringify(variables, null, 2);
|
|
8246
|
+
const truncated = fullVariables.length > VARIABLES_BUDGET;
|
|
8247
|
+
const variablesJson = truncated ? `${fullVariables.slice(0, VARIABLES_BUDGET)}
|
|
8248
|
+
\u2026[truncated \u2014 ${fullVariables.length - VARIABLES_BUDGET} more bytes elided]` : fullVariables;
|
|
8201
8249
|
return [
|
|
8202
8250
|
"Recreate a StreamElements overlay widget as a Lumia Stream custom overlay (HTML + CSS + JS).",
|
|
8203
8251
|
"",
|
|
@@ -8205,10 +8253,11 @@ function buildAIPromptForSEWidget(widget, canvas) {
|
|
|
8205
8253
|
listener ? `Primary event listener: ${listener}` : "",
|
|
8206
8254
|
listeners ? `Subscribes to events: ${listeners.join(", ")}` : "",
|
|
8207
8255
|
`Canvas: ${landing.width} \xD7 ${landing.height}`,
|
|
8256
|
+
truncated ? "Note: the widget config below is truncated \u2014 focus on visible/structural fields and ignore tail content you cannot see." : "",
|
|
8208
8257
|
"",
|
|
8209
8258
|
"Widget configuration (JSON):",
|
|
8210
8259
|
"```json",
|
|
8211
|
-
|
|
8260
|
+
variablesJson,
|
|
8212
8261
|
"```",
|
|
8213
8262
|
"",
|
|
8214
8263
|
"Constraints \u2014 your output must run inside a Lumia Custom Overlay sandboxed iframe:",
|
|
@@ -8244,7 +8293,6 @@ export {
|
|
|
8244
8293
|
SEAuthError,
|
|
8245
8294
|
SEClient,
|
|
8246
8295
|
SEImportWizard,
|
|
8247
|
-
SE_IMPORT_FLAGS,
|
|
8248
8296
|
SE_WIDGET_TO_MARKETPLACE_CANDIDATES,
|
|
8249
8297
|
applyReviewAction,
|
|
8250
8298
|
buildAIPromptForSEWidget,
|