@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/se-import.js
CHANGED
|
@@ -90,10 +90,11 @@ function defaultLabelForSEWidget(widget) {
|
|
|
90
90
|
const tail = words.slice(1).join(" ");
|
|
91
91
|
return tail ? `${head} ${tail}` : head;
|
|
92
92
|
}
|
|
93
|
-
function translateSeText(input, listener) {
|
|
93
|
+
function translateSeText(input, listener, passthrough) {
|
|
94
94
|
if (!input) return "";
|
|
95
95
|
const ctx = listener ? listenerPlaceholderContext(listener) : {};
|
|
96
96
|
return input.replace(/\{([a-zA-Z][a-zA-Z0-9_.]*)\}/g, (_match, raw) => {
|
|
97
|
+
if (passthrough?.has(raw)) return `{{${raw}}}`;
|
|
97
98
|
const mapped = ctx[raw] ?? SE_TO_LUMIA_PLACEHOLDER[raw] ?? raw;
|
|
98
99
|
return `{{${mapped}}}`;
|
|
99
100
|
});
|
|
@@ -287,14 +288,10 @@ function seLayoutUsesPushText(seLayout) {
|
|
|
287
288
|
|
|
288
289
|
// src/se-import/mappers/build.ts
|
|
289
290
|
import { nanoid } from "nanoid";
|
|
290
|
-
|
|
291
|
-
function setImportCanvas(canvas) {
|
|
292
|
-
currentImportCanvas = canvas;
|
|
293
|
-
}
|
|
294
|
-
function buildUnit(widget, lumiaType, moduleExtras) {
|
|
291
|
+
function buildUnit(widget, lumiaType, moduleExtras, ctx) {
|
|
295
292
|
const id = nanoid();
|
|
296
293
|
const defaults = LUMIA_DEFAULT_SIZES[lumiaType] ?? { width: 400, height: 400 };
|
|
297
|
-
const bounds = seCssToBounds(widget.css, defaults,
|
|
294
|
+
const bounds = seCssToBounds(widget.css, defaults, ctx?.canvas);
|
|
298
295
|
const layer = {
|
|
299
296
|
id,
|
|
300
297
|
state: { visible: widget.visible ?? true, locked: widget.locked ?? false },
|
|
@@ -346,7 +343,7 @@ function seExtraTextCss(seCss, scrolling) {
|
|
|
346
343
|
}
|
|
347
344
|
return extra;
|
|
348
345
|
}
|
|
349
|
-
function mapText(widget) {
|
|
346
|
+
function mapText(widget, ctx) {
|
|
350
347
|
const value = translateSeText(widget.text?.value ?? "");
|
|
351
348
|
const seCss = widget.text?.css ?? {};
|
|
352
349
|
return buildUnit(widget, "text", {
|
|
@@ -365,17 +362,17 @@ function mapText(widget) {
|
|
|
365
362
|
background: "transparent",
|
|
366
363
|
...seExtraTextCss(seCss, widget.text?.scrolling)
|
|
367
364
|
}
|
|
368
|
-
});
|
|
365
|
+
}, ctx);
|
|
369
366
|
}
|
|
370
|
-
function mapImage(widget) {
|
|
367
|
+
function mapImage(widget, ctx) {
|
|
371
368
|
return buildUnit(widget, "image", {
|
|
372
369
|
content: {
|
|
373
370
|
src: widget.image?.src ?? "",
|
|
374
371
|
loop: true
|
|
375
372
|
}
|
|
376
|
-
});
|
|
373
|
+
}, ctx);
|
|
377
374
|
}
|
|
378
|
-
function mapVideo(widget) {
|
|
375
|
+
function mapVideo(widget, ctx) {
|
|
379
376
|
return buildUnit(widget, "video", {
|
|
380
377
|
content: {
|
|
381
378
|
src: widget.video?.src ?? "",
|
|
@@ -383,10 +380,10 @@ function mapVideo(widget) {
|
|
|
383
380
|
loop: true,
|
|
384
381
|
muted: false
|
|
385
382
|
}
|
|
386
|
-
});
|
|
383
|
+
}, ctx);
|
|
387
384
|
}
|
|
388
385
|
var LUMIA_SNOW_WEBM = "https://storage.lumiastream.com/overlays/global/seasonal/snow.webm";
|
|
389
|
-
function mapSnow(widget) {
|
|
386
|
+
function mapSnow(widget, ctx) {
|
|
390
387
|
return buildUnit(widget, "video", {
|
|
391
388
|
content: {
|
|
392
389
|
src: LUMIA_SNOW_WEBM,
|
|
@@ -394,9 +391,9 @@ function mapSnow(widget) {
|
|
|
394
391
|
loop: true,
|
|
395
392
|
muted: true
|
|
396
393
|
}
|
|
397
|
-
});
|
|
394
|
+
}, ctx);
|
|
398
395
|
}
|
|
399
|
-
function mapReadout(widget, fallbackVar) {
|
|
396
|
+
function mapReadout(widget, fallbackVar, ctx) {
|
|
400
397
|
const explicitListener = widget.variables?.listener;
|
|
401
398
|
const explicit = typeof explicitListener === "string" ? explicitListener : null;
|
|
402
399
|
const widgetListener = typeof widget.listener === "string" ? widget.listener : null;
|
|
@@ -419,7 +416,7 @@ function mapReadout(widget, fallbackVar) {
|
|
|
419
416
|
background: "transparent",
|
|
420
417
|
...seExtraTextCss(seCss, widget.text?.scrolling)
|
|
421
418
|
}
|
|
422
|
-
});
|
|
419
|
+
}, ctx);
|
|
423
420
|
}
|
|
424
421
|
|
|
425
422
|
// src/se-import/mappers/alert.ts
|
|
@@ -451,13 +448,14 @@ var SE_EVENT_TO_LUMIA_ALERT = {
|
|
|
451
448
|
// mapping prevents misleading "channel-point redemption" alerts.
|
|
452
449
|
};
|
|
453
450
|
function pickGraphics(g) {
|
|
454
|
-
|
|
451
|
+
const DEFAULT_GRAPHIC_VOLUME = 0.5;
|
|
452
|
+
if (!g) return { type: "image", content: { src: "", loop: false, volume: DEFAULT_GRAPHIC_VOLUME } };
|
|
455
453
|
return {
|
|
456
454
|
type: g.type === "video" ? "video" : "image",
|
|
457
455
|
content: {
|
|
458
456
|
src: g.src ?? "",
|
|
459
457
|
loop: false,
|
|
460
|
-
volume: g.volume ??
|
|
458
|
+
volume: g.volume ?? DEFAULT_GRAPHIC_VOLUME
|
|
461
459
|
}
|
|
462
460
|
};
|
|
463
461
|
}
|
|
@@ -628,7 +626,7 @@ function pickBaseAlertFields(event) {
|
|
|
628
626
|
const { on: _on, variations: _variations, matchEmptyCondition: _mec, ...base } = event;
|
|
629
627
|
return base;
|
|
630
628
|
}
|
|
631
|
-
function mapAlertBox(widget, opts = {}) {
|
|
629
|
+
function mapAlertBox(widget, opts = {}, ctx) {
|
|
632
630
|
const v = widget.variables ?? {};
|
|
633
631
|
const events = {};
|
|
634
632
|
const eventKeys = opts.onlyEvents ?? ["follower", "subscriber", "tip", "cheer", "raid", "merch", "purchase", "charityCampaignDonation"];
|
|
@@ -643,9 +641,9 @@ function mapAlertBox(widget, opts = {}) {
|
|
|
643
641
|
if (firstEvent == null) firstEvent = built;
|
|
644
642
|
}
|
|
645
643
|
const baseAlert = firstEvent ? pickBaseAlertFields(firstEvent) : {};
|
|
646
|
-
return buildUnit(widget, "alert", { alert: { ...baseAlert, events } });
|
|
644
|
+
return buildUnit(widget, "alert", { alert: { ...baseAlert, events } }, ctx);
|
|
647
645
|
}
|
|
648
|
-
function mapSingleAlert(widget, lumiaAlertKey) {
|
|
646
|
+
function mapSingleAlert(widget, lumiaAlertKey, ctx) {
|
|
649
647
|
const v = widget.variables ?? {};
|
|
650
648
|
const topText = widget.text;
|
|
651
649
|
const merged = { ...v };
|
|
@@ -669,7 +667,7 @@ function mapSingleAlert(widget, lumiaAlertKey) {
|
|
|
669
667
|
[lumiaAlertKey]: built
|
|
670
668
|
};
|
|
671
669
|
const baseAlert = pickBaseAlertFields(built);
|
|
672
|
-
return buildUnit(widget, "alert", { alert: { ...baseAlert, events } });
|
|
670
|
+
return buildUnit(widget, "alert", { alert: { ...baseAlert, events } }, ctx);
|
|
673
671
|
}
|
|
674
672
|
|
|
675
673
|
// src/se-import/mappers/goal.ts
|
|
@@ -707,7 +705,7 @@ function sourceFromListener(listener) {
|
|
|
707
705
|
if (listener.startsWith("merch-")) return "merch_orders";
|
|
708
706
|
return "custom";
|
|
709
707
|
}
|
|
710
|
-
function mapGoal(widget) {
|
|
708
|
+
function mapGoal(widget, ctx) {
|
|
711
709
|
const v = widget.variables ?? {};
|
|
712
710
|
const listener = v.goalListener ?? (typeof widget.listener === "string" ? widget.listener : null);
|
|
713
711
|
const charity = charityGoalVariables(widget.type);
|
|
@@ -768,7 +766,7 @@ function mapGoal(widget) {
|
|
|
768
766
|
textShadow: seTextCss["text-shadow"] ?? "rgb(0, 0, 0) 1px 1px 1px",
|
|
769
767
|
background: "transparent"
|
|
770
768
|
}
|
|
771
|
-
});
|
|
769
|
+
}, ctx);
|
|
772
770
|
}
|
|
773
771
|
|
|
774
772
|
// src/se-import/mappers/custom.ts
|
|
@@ -780,19 +778,46 @@ var SE_FIELD_TYPE_TO_LUMIA = {
|
|
|
780
778
|
slider: "slider",
|
|
781
779
|
colorpicker: "colorpicker",
|
|
782
780
|
fontpicker: "fontpicker",
|
|
783
|
-
|
|
781
|
+
// SE's font picker variant — same control surface as `fontpicker` for our
|
|
782
|
+
// purposes; the SE renderer pulls Google Fonts at runtime which Lumia's
|
|
783
|
+
// fontpicker also does.
|
|
784
|
+
googleFont: "fontpicker",
|
|
785
|
+
// SE asset uploads — the asset URL lands in the `value` as a CDN string,
|
|
786
|
+
// which our `input` renders fine as-is. Once Lumia ships dedicated asset/
|
|
787
|
+
// audio pickers wired through the asset library, flip these mappings.
|
|
788
|
+
image: "input",
|
|
789
|
+
"image-input": "input",
|
|
790
|
+
"sound-input": "input",
|
|
791
|
+
"video-input": "input",
|
|
792
|
+
// Multi-line text — `input` accepts strings of any length; the editor
|
|
793
|
+
// surfaces a textarea by inspecting `value.length` heuristically.
|
|
794
|
+
textarea: "input",
|
|
795
|
+
hidden: "input",
|
|
796
|
+
// SE's button is a "fire a JS action" trigger with no persisted value.
|
|
797
|
+
// Lumia has no equivalent; surface as a non-editable label.
|
|
798
|
+
button: "input"
|
|
784
799
|
};
|
|
800
|
+
var SE_RESERVED_FIELD_KEYS = /* @__PURE__ */ new Set(["widgetName", "widgetAuthor", "widgetDuration"]);
|
|
785
801
|
function translateConfigs(seFields) {
|
|
786
802
|
if (!seFields) return [];
|
|
787
|
-
return Object.entries(seFields).map(([key, f]) =>
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
803
|
+
return Object.entries(seFields).filter(([key]) => !SE_RESERVED_FIELD_KEYS.has(key)).map(([key, f]) => {
|
|
804
|
+
const seType = f.type ?? "text";
|
|
805
|
+
return {
|
|
806
|
+
key,
|
|
807
|
+
label: f.label ?? key,
|
|
808
|
+
type: SE_FIELD_TYPE_TO_LUMIA[seType] ?? "input",
|
|
809
|
+
value: f.value,
|
|
810
|
+
options: f.options,
|
|
811
|
+
// Preserve the original SE type + group so a future editor UX
|
|
812
|
+
// upgrade can render the right control (e.g. an actual asset
|
|
813
|
+
// picker for `image-input`) without re-importing. Mentioned in
|
|
814
|
+
// docs/SE_IMPORT_GAPS.md under "Other widgets > custom-event-list".
|
|
815
|
+
seType,
|
|
816
|
+
...f.group ? { seGroup: f.group } : {}
|
|
817
|
+
};
|
|
818
|
+
});
|
|
794
819
|
}
|
|
795
|
-
function mapCustom(widget) {
|
|
820
|
+
function mapCustom(widget, ctx) {
|
|
796
821
|
const v = widget.variables ?? {};
|
|
797
822
|
const parse = (x) => {
|
|
798
823
|
if (x == null) return void 0;
|
|
@@ -815,7 +840,7 @@ function mapCustom(widget) {
|
|
|
815
840
|
data: fieldData ?? {},
|
|
816
841
|
flavor: "streamelements"
|
|
817
842
|
}
|
|
818
|
-
});
|
|
843
|
+
}, ctx);
|
|
819
844
|
}
|
|
820
845
|
|
|
821
846
|
// src/se-import/mappers/misc.ts
|
|
@@ -843,7 +868,7 @@ function paddingShorthand(message) {
|
|
|
843
868
|
const px = (n) => `${n ?? 0}px`;
|
|
844
869
|
return `${px(t)} ${px(r)} ${px(b)} ${px(l)}`;
|
|
845
870
|
}
|
|
846
|
-
function mapChatbox(widget) {
|
|
871
|
+
function mapChatbox(widget, ctx) {
|
|
847
872
|
const v = widget.variables ?? {};
|
|
848
873
|
const textCss = widget.text?.css ?? {};
|
|
849
874
|
const messageCss = textCss.message ?? {};
|
|
@@ -927,7 +952,7 @@ function mapChatbox(widget) {
|
|
|
927
952
|
se_highlight_users: v.highlight,
|
|
928
953
|
se_text_css: textCss
|
|
929
954
|
}
|
|
930
|
-
});
|
|
955
|
+
}, ctx);
|
|
931
956
|
}
|
|
932
957
|
var SE_LISTENER_TO_LUMIA_CATEGORY = {
|
|
933
958
|
"follower-latest": "follower",
|
|
@@ -993,7 +1018,7 @@ function computeListenerFilters(seListeners, explicitListener) {
|
|
|
993
1018
|
if (allowed.size === 0) return [];
|
|
994
1019
|
return ALL_LUMIA_EVENTLIST_CATEGORIES.filter((c) => !allowed.has(c));
|
|
995
1020
|
}
|
|
996
|
-
function mapEventList(widget) {
|
|
1021
|
+
function mapEventList(widget, ctx) {
|
|
997
1022
|
const v = widget.variables ?? {};
|
|
998
1023
|
const filters = computeListenerFilters(widget.listeners, widget.listener);
|
|
999
1024
|
const maxItems = v.visibleEvents ?? v.visibleItems ?? 5;
|
|
@@ -1022,7 +1047,7 @@ function mapEventList(widget) {
|
|
|
1022
1047
|
showDetailedText: false,
|
|
1023
1048
|
hideAlertMessage: false
|
|
1024
1049
|
}
|
|
1025
|
-
});
|
|
1050
|
+
}, ctx);
|
|
1026
1051
|
}
|
|
1027
1052
|
var SE_CREDITS_EVENTS_TO_SHOW = {
|
|
1028
1053
|
mods: false,
|
|
@@ -1162,7 +1187,7 @@ var CREDITS_DEFAULT_TEMPLATES = {
|
|
|
1162
1187
|
topSuperchats: "{{username}} x{{amount}}",
|
|
1163
1188
|
topRedemptions: "{{username}} x{{amount}}"
|
|
1164
1189
|
};
|
|
1165
|
-
function mapCredits(widget) {
|
|
1190
|
+
function mapCredits(widget, ctx) {
|
|
1166
1191
|
const v = widget.variables ?? {};
|
|
1167
1192
|
const itemGap = typeof v.padding === "number" ? v.padding : 8;
|
|
1168
1193
|
const reverseFlow = v.direction === "bottom";
|
|
@@ -1216,7 +1241,7 @@ function mapCredits(widget) {
|
|
|
1216
1241
|
textAlign: "center",
|
|
1217
1242
|
background: "transparent"
|
|
1218
1243
|
}
|
|
1219
|
-
});
|
|
1244
|
+
}, ctx);
|
|
1220
1245
|
}
|
|
1221
1246
|
function inferSlideshowMediaType(mime, url) {
|
|
1222
1247
|
if (typeof mime === "string") {
|
|
@@ -1235,7 +1260,7 @@ function nameFromUrl(url) {
|
|
|
1235
1260
|
return url;
|
|
1236
1261
|
}
|
|
1237
1262
|
}
|
|
1238
|
-
function mapSlideshow(widget) {
|
|
1263
|
+
function mapSlideshow(widget, ctx) {
|
|
1239
1264
|
const v = widget.variables ?? {};
|
|
1240
1265
|
const items = (v.images ?? []).map((raw) => {
|
|
1241
1266
|
const url = typeof raw === "string" ? raw : raw.url ?? raw.src ?? "";
|
|
@@ -1275,21 +1300,13 @@ function mapSlideshow(widget) {
|
|
|
1275
1300
|
se_interval: v.interval,
|
|
1276
1301
|
se_raw_duration: v.duration
|
|
1277
1302
|
}
|
|
1278
|
-
});
|
|
1303
|
+
}, ctx);
|
|
1279
1304
|
}
|
|
1280
|
-
function mapTimer(widget) {
|
|
1305
|
+
function mapTimer(widget, ctx) {
|
|
1281
1306
|
const v = widget.variables ?? {};
|
|
1282
1307
|
const textValue = typeof widget.text?.value === "string" ? widget.text.value : void 0;
|
|
1283
|
-
const
|
|
1284
|
-
|
|
1285
|
-
/\{(days|hours|minutes|seconds)\}/g,
|
|
1286
|
-
"__SE_COUNTDOWN_$1__"
|
|
1287
|
-
);
|
|
1288
|
-
return translateSeText(protectedTemplate).replace(
|
|
1289
|
-
/__SE_COUNTDOWN_(days|hours|minutes|seconds)__/g,
|
|
1290
|
-
"{{$1}}"
|
|
1291
|
-
);
|
|
1292
|
-
};
|
|
1308
|
+
const TIMER_PASSTHROUGH = /* @__PURE__ */ new Set(["days", "hours", "minutes", "seconds"]);
|
|
1309
|
+
const mapTemplate = (template) => translateSeText(template, null, TIMER_PASSTHROUGH);
|
|
1293
1310
|
const absoluteDate = v.date ?? v.endsAt ?? v.endDate;
|
|
1294
1311
|
const target = absoluteDate ? Date.parse(absoluteDate) : NaN;
|
|
1295
1312
|
const hasValidAbsoluteDate = Number.isFinite(target);
|
|
@@ -1365,9 +1382,9 @@ function mapTimer(widget) {
|
|
|
1365
1382
|
se_date_picker: v.datePicker,
|
|
1366
1383
|
se_exact_time: v.exactTime
|
|
1367
1384
|
}
|
|
1368
|
-
});
|
|
1385
|
+
}, ctx);
|
|
1369
1386
|
}
|
|
1370
|
-
function mapNowPlaying(widget) {
|
|
1387
|
+
function mapNowPlaying(widget, ctx) {
|
|
1371
1388
|
const v = widget.variables ?? {};
|
|
1372
1389
|
const source = v.provider === "youtube" ? "youtubemusic" : "spotify";
|
|
1373
1390
|
return buildUnit(widget, "nowplaying", {
|
|
@@ -1397,9 +1414,9 @@ function mapNowPlaying(widget) {
|
|
|
1397
1414
|
textShadow: "rgb(0, 0, 0) 1px 1px 1px",
|
|
1398
1415
|
background: "transparent"
|
|
1399
1416
|
}
|
|
1400
|
-
});
|
|
1417
|
+
}, ctx);
|
|
1401
1418
|
}
|
|
1402
|
-
function mapMediaShare(widget) {
|
|
1419
|
+
function mapMediaShare(widget, ctx) {
|
|
1403
1420
|
const v = widget.variables ?? {};
|
|
1404
1421
|
return buildUnit(widget, "songrequest", {
|
|
1405
1422
|
content: {
|
|
@@ -1440,7 +1457,7 @@ function mapMediaShare(widget) {
|
|
|
1440
1457
|
background: "transparent",
|
|
1441
1458
|
fontSize: 18
|
|
1442
1459
|
}
|
|
1443
|
-
});
|
|
1460
|
+
}, ctx);
|
|
1444
1461
|
}
|
|
1445
1462
|
var SE_KAPPAGEN_EVENT_TO_LUMIA_ALERT = {
|
|
1446
1463
|
follower: "twitch-follower",
|
|
@@ -1463,7 +1480,7 @@ var SE_KAPPAGEN_EVENT_TO_LUMIA_ALERT = {
|
|
|
1463
1480
|
// All three drop silently; the rest of the widget (size, spawn zone,
|
|
1464
1481
|
// per-event flags for the remaining events) still imports.
|
|
1465
1482
|
};
|
|
1466
|
-
function mapKappagen(widget) {
|
|
1483
|
+
function mapKappagen(widget, ctx) {
|
|
1467
1484
|
const v = widget.variables ?? {};
|
|
1468
1485
|
const eventsFromNested = v.eventsConfig ? {
|
|
1469
1486
|
subscriber: v.eventsConfig.subscriber?.enabled ?? true,
|
|
@@ -1515,10 +1532,11 @@ function mapKappagen(widget) {
|
|
|
1515
1532
|
se_direction: v.direction
|
|
1516
1533
|
},
|
|
1517
1534
|
css: { background: "transparent" }
|
|
1518
|
-
}
|
|
1535
|
+
},
|
|
1536
|
+
ctx
|
|
1519
1537
|
);
|
|
1520
1538
|
}
|
|
1521
|
-
function mapLeaderboardFromVariables(widget, currency) {
|
|
1539
|
+
function mapLeaderboardFromVariables(widget, currency, ctx) {
|
|
1522
1540
|
const v = widget.variables ?? {};
|
|
1523
1541
|
const count = v.limit ?? v.visibleItems ?? 10;
|
|
1524
1542
|
const window = v.period === "weekly" ? "week" : v.period === "monthly" ? "month" : "alltime";
|
|
@@ -1541,9 +1559,9 @@ function mapLeaderboardFromVariables(widget, currency) {
|
|
|
1541
1559
|
se_offset: v.offset,
|
|
1542
1560
|
se_layout: v.layout
|
|
1543
1561
|
}
|
|
1544
|
-
});
|
|
1562
|
+
}, ctx);
|
|
1545
1563
|
}
|
|
1546
|
-
function mapGiveaway(widget) {
|
|
1564
|
+
function mapGiveaway(widget, ctx) {
|
|
1547
1565
|
const v = widget.variables ?? {};
|
|
1548
1566
|
const primary = v.colors?.primary ?? "#393853";
|
|
1549
1567
|
const accent = v.colors?.accent ?? "#FF4076";
|
|
@@ -1594,7 +1612,7 @@ function mapGiveaway(widget) {
|
|
|
1594
1612
|
se_display_tickets_purchased: v.displayTicketsPurchased,
|
|
1595
1613
|
se_image_style_width: v.imageStyle?.width
|
|
1596
1614
|
}
|
|
1597
|
-
});
|
|
1615
|
+
}, ctx);
|
|
1598
1616
|
}
|
|
1599
1617
|
var HYPETRAIN_LISTENER_KEYS = /* @__PURE__ */ new Set([
|
|
1600
1618
|
"hypetrain-latest",
|
|
@@ -1625,7 +1643,7 @@ function isHypetrainCustomWidget(widget) {
|
|
|
1625
1643
|
if (enabled.length === 0) return false;
|
|
1626
1644
|
return enabled.every((listener) => HYPETRAIN_LISTENER_KEYS.has(listener));
|
|
1627
1645
|
}
|
|
1628
|
-
function mapHypetrain(widget) {
|
|
1646
|
+
function mapHypetrain(widget, ctx) {
|
|
1629
1647
|
const v = widget.variables ?? {};
|
|
1630
1648
|
return buildUnit(widget, "hypetrain", {
|
|
1631
1649
|
content: {
|
|
@@ -1668,9 +1686,9 @@ function mapHypetrain(widget) {
|
|
|
1668
1686
|
// these via the overlay JSON if they want to recreate a specific look.
|
|
1669
1687
|
se_variables: v
|
|
1670
1688
|
}
|
|
1671
|
-
});
|
|
1689
|
+
}, ctx);
|
|
1672
1690
|
}
|
|
1673
|
-
function mapUnsupportedAsText(widget) {
|
|
1691
|
+
function mapUnsupportedAsText(widget, ctx) {
|
|
1674
1692
|
return buildUnit(widget, "text", {
|
|
1675
1693
|
content: {
|
|
1676
1694
|
value: `[Unsupported SE widget: ${widget.type}]`,
|
|
@@ -1684,7 +1702,7 @@ function mapUnsupportedAsText(widget) {
|
|
|
1684
1702
|
fontWeight: "bold",
|
|
1685
1703
|
background: "rgba(0,0,0,0.4)"
|
|
1686
1704
|
}
|
|
1687
|
-
});
|
|
1705
|
+
}, ctx);
|
|
1688
1706
|
}
|
|
1689
1707
|
|
|
1690
1708
|
// src/se-import/mappers/hypecup.ts
|
|
@@ -1754,7 +1772,7 @@ function categoriesFromListeners(listeners) {
|
|
|
1754
1772
|
if (events.subs) events.resubs = true;
|
|
1755
1773
|
return events;
|
|
1756
1774
|
}
|
|
1757
|
-
function mapHypeCup(widget) {
|
|
1775
|
+
function mapHypeCup(widget, ctx) {
|
|
1758
1776
|
const v = widget.variables ?? {};
|
|
1759
1777
|
const widgetWidth = typeof v.width === "number" && v.width > 0 ? v.width : parsePx(widget.css?.width);
|
|
1760
1778
|
const widgetHeight = typeof v.height === "number" && v.height > 0 ? v.height : parsePx(widget.css?.height);
|
|
@@ -1812,7 +1830,7 @@ function mapHypeCup(widget) {
|
|
|
1812
1830
|
css: {
|
|
1813
1831
|
background: "transparent"
|
|
1814
1832
|
}
|
|
1815
|
-
});
|
|
1833
|
+
}, ctx);
|
|
1816
1834
|
}
|
|
1817
1835
|
function clamp01(n) {
|
|
1818
1836
|
if (!Number.isFinite(n)) return 0.5;
|
|
@@ -1859,7 +1877,7 @@ function listenerIsOn(listeners, key) {
|
|
|
1859
1877
|
if (Array.isArray(listeners)) return listeners.includes(key);
|
|
1860
1878
|
return listeners[key] === true;
|
|
1861
1879
|
}
|
|
1862
|
-
function mapStreamBoss(widget) {
|
|
1880
|
+
function mapStreamBoss(widget, ctx) {
|
|
1863
1881
|
const v = widget.variables ?? {};
|
|
1864
1882
|
const baseHp = typeof v.baseHP === "number" && v.baseHP > 0 ? Math.round(v.baseHP) : 1e3;
|
|
1865
1883
|
const seDamage = v.damage ?? {};
|
|
@@ -1941,7 +1959,7 @@ function mapStreamBoss(widget) {
|
|
|
1941
1959
|
css: {
|
|
1942
1960
|
background: "transparent"
|
|
1943
1961
|
}
|
|
1944
|
-
});
|
|
1962
|
+
}, ctx);
|
|
1945
1963
|
if (theme === "card") {
|
|
1946
1964
|
const MIN_CARD_WIDTH = 480;
|
|
1947
1965
|
const MIN_CARD_HEIGHT = 110;
|
|
@@ -1952,25 +1970,6 @@ function mapStreamBoss(widget) {
|
|
|
1952
1970
|
}
|
|
1953
1971
|
|
|
1954
1972
|
// src/se-import/feature-flags.ts
|
|
1955
|
-
var SE_IMPORT_FLAGS = {
|
|
1956
|
-
// (Removed) IMPORT_CUSTOM_EMBED: custom-event-list now routes to native eventlist
|
|
1957
|
-
// by default with a one-click "switch to custom HTML" option in the review step.
|
|
1958
|
-
// (Removed) IMPORT_MERCH_GOAL: Fourthwall integration provides session/total order
|
|
1959
|
-
// counters now (see fourthwall.manager.ts ORDER_PLACED handler); merch goal maps
|
|
1960
|
-
// to {{fourthwall_total_*}} via the goal mapper (per-listener: orders → order_count,
|
|
1961
|
-
// items → items_count, total → order_amount).
|
|
1962
|
-
// (Removed) IMPORT_SEASONAL: seasonal widgets now route per-widget into existing
|
|
1963
|
-
// modules (image / slideshow / video) rather than a single approximate canvas-
|
|
1964
|
-
// particle template. See dispatcher.ts SEASONAL_IMAGE_TYPES comment for the
|
|
1965
|
-
// rationale and /SE/05-widget-to-module-mapping.md §D for the current mapping.
|
|
1966
|
-
// (Removed) IMPORT_MERCH_PRODUCTS: the merch-products-rotator now imports as an
|
|
1967
|
-
// empty slideshow with a friendly layer title prompting the user to add product
|
|
1968
|
-
// images manually. This is a degraded but honest UX — better than a placeholder
|
|
1969
|
-
// text layer for the (rare) streamer who uses SE's merch rotator. A Fourthwall
|
|
1970
|
-
// catalog fetcher would unlock fully-automatic import, but that's a multi-day
|
|
1971
|
-
// project gated on a new Fourthwall API auth scope, an endpoint, and a refresh
|
|
1972
|
-
// cadence.
|
|
1973
|
-
};
|
|
1974
1973
|
function isWidgetFlaggedOff(_seType) {
|
|
1975
1974
|
return false;
|
|
1976
1975
|
}
|
|
@@ -2025,7 +2024,7 @@ var GOAL_TYPES = /* @__PURE__ */ new Set([
|
|
|
2025
2024
|
"se-widget-merch-goal"
|
|
2026
2025
|
]);
|
|
2027
2026
|
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"]);
|
|
2028
|
-
function dispatch(widget) {
|
|
2027
|
+
function dispatch(widget, ctx) {
|
|
2029
2028
|
const t = widget.type;
|
|
2030
2029
|
console.log("[SE-IMPORT] dispatch widget", {
|
|
2031
2030
|
seType: t,
|
|
@@ -2036,46 +2035,46 @@ function dispatch(widget) {
|
|
|
2036
2035
|
hasCss: !!widget.css
|
|
2037
2036
|
});
|
|
2038
2037
|
if (isWidgetFlaggedOff(t)) {
|
|
2039
|
-
return { unit: mapUnsupportedAsText(widget), status: "placeholder", lumiaType: "text", flaggedOff: true };
|
|
2038
|
+
return { unit: mapUnsupportedAsText(widget, ctx), status: "placeholder", lumiaType: "text", flaggedOff: true };
|
|
2040
2039
|
}
|
|
2041
|
-
if (t === "text") return { unit: mapText(widget), status: "direct", lumiaType: "text" };
|
|
2042
|
-
if (t === "image") return { unit: mapImage(widget), status: "direct", lumiaType: "image" };
|
|
2043
|
-
if (t === "video") return { unit: mapVideo(widget), status: "direct", lumiaType: "video" };
|
|
2040
|
+
if (t === "text") return { unit: mapText(widget, ctx), status: "direct", lumiaType: "text" };
|
|
2041
|
+
if (t === "image") return { unit: mapImage(widget, ctx), status: "direct", lumiaType: "image" };
|
|
2042
|
+
if (t === "video") return { unit: mapVideo(widget, ctx), status: "direct", lumiaType: "video" };
|
|
2044
2043
|
if (t in READOUT_TYPES) {
|
|
2045
|
-
return { unit: mapReadout(widget, READOUT_TYPES[t]), status: "direct", lumiaType: "text" };
|
|
2044
|
+
return { unit: mapReadout(widget, READOUT_TYPES[t], ctx), status: "direct", lumiaType: "text" };
|
|
2046
2045
|
}
|
|
2047
|
-
if (GOAL_TYPES.has(t)) return { unit: mapGoal(widget), status: "direct", lumiaType: "goal" };
|
|
2046
|
+
if (GOAL_TYPES.has(t)) return { unit: mapGoal(widget, ctx), status: "direct", lumiaType: "goal" };
|
|
2048
2047
|
if ((t === "se-widget-alert-box" || t === "se-widget-alert-box-merch") && hasSECustomCode(widget)) {
|
|
2049
|
-
return { unit: mapUnsupportedAsText(widget), status: "placeholder", lumiaType: "text", flaggedOff: true };
|
|
2048
|
+
return { unit: mapUnsupportedAsText(widget, ctx), status: "placeholder", lumiaType: "text", flaggedOff: true };
|
|
2050
2049
|
}
|
|
2051
|
-
if (t === "se-widget-alert-box") return { unit: mapAlertBox(widget), status: "direct", lumiaType: "alert" };
|
|
2052
|
-
if (t === "se-widget-alert-box-merch") return { unit: mapAlertBox(widget, { onlyEvents: ["merch"] }), status: "direct", lumiaType: "alert" };
|
|
2053
|
-
if (t === "se-widget-donor-drive-alert") return { unit: mapSingleAlert(widget, "donordrive-donation"), status: "direct", lumiaType: "alert" };
|
|
2054
|
-
if (t === "se-widget-extralife-alert") return { unit: mapSingleAlert(widget, "extralife-donation"), status: "direct", lumiaType: "alert" };
|
|
2055
|
-
if (t === "se-widget-tiltify-alert") return { unit: mapSingleAlert(widget, "tiltify-campaignDonation"), status: "direct", lumiaType: "alert" };
|
|
2056
|
-
if (EVENTLIST_TYPES.has(t)) return { unit: mapEventList(widget), status: "direct", lumiaType: "eventlist" };
|
|
2057
|
-
if (t === "se-widget-top-tippers-list") return { unit: mapLeaderboardFromVariables(widget, "tips"), status: "direct", lumiaType: "loyaltyleaderboard" };
|
|
2058
|
-
if (t === "se-widget-top-cheerers-list") return { unit: mapLeaderboardFromVariables(widget, "cheers"), status: "direct", lumiaType: "loyaltyleaderboard" };
|
|
2059
|
-
if (t === "se-widget-top-gifters-list") return { unit: mapLeaderboardFromVariables(widget, "gifts"), status: "direct", lumiaType: "loyaltyleaderboard" };
|
|
2060
|
-
if (t === "se-widget-credit-roll") return { unit: mapCredits(widget), status: "partial", lumiaType: "credits" };
|
|
2061
|
-
if (t === "se-widget-twitch-chat") return { unit: mapChatbox(widget), status: "partial", lumiaType: "chatbox" };
|
|
2062
|
-
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" };
|
|
2063
|
-
if (t === "se-widget-countdown") return { unit: mapTimer(widget), status: "partial", lumiaType: "timer" };
|
|
2064
|
-
if (t === "se-widget-current-song") return { unit: mapNowPlaying(widget), status: "partial", lumiaType: "nowplaying" };
|
|
2065
|
-
if (t === "se-widget-kappagen") return { unit: mapKappagen(widget), status: "partial", lumiaType: "emotealert" };
|
|
2066
|
-
if (t === "se-widget-giveaway") return { unit: mapGiveaway(widget), status: "direct", lumiaType: "raffle" };
|
|
2067
|
-
if (t === "se-widget-media-share") return { unit: mapMediaShare(widget), status: "partial", lumiaType: "songrequest" };
|
|
2068
|
-
if (t === "se-widget-hype-cup") return { unit: mapHypeCup(widget), status: "partial", lumiaType: "tipjar" };
|
|
2069
|
-
if (t === "se-widget-bit-boss") return { unit: mapStreamBoss(widget), status: "partial", lumiaType: "streamboss" };
|
|
2070
|
-
if (t === "se-widget-hype-train") return { unit: mapHypetrain(widget), status: "partial", lumiaType: "hypetrain" };
|
|
2050
|
+
if (t === "se-widget-alert-box") return { unit: mapAlertBox(widget, {}, ctx), status: "direct", lumiaType: "alert" };
|
|
2051
|
+
if (t === "se-widget-alert-box-merch") return { unit: mapAlertBox(widget, { onlyEvents: ["merch"] }, ctx), status: "direct", lumiaType: "alert" };
|
|
2052
|
+
if (t === "se-widget-donor-drive-alert") return { unit: mapSingleAlert(widget, "donordrive-donation", ctx), status: "direct", lumiaType: "alert" };
|
|
2053
|
+
if (t === "se-widget-extralife-alert") return { unit: mapSingleAlert(widget, "extralife-donation", ctx), status: "direct", lumiaType: "alert" };
|
|
2054
|
+
if (t === "se-widget-tiltify-alert") return { unit: mapSingleAlert(widget, "tiltify-campaignDonation", ctx), status: "direct", lumiaType: "alert" };
|
|
2055
|
+
if (EVENTLIST_TYPES.has(t)) return { unit: mapEventList(widget, ctx), status: "direct", lumiaType: "eventlist" };
|
|
2056
|
+
if (t === "se-widget-top-tippers-list") return { unit: mapLeaderboardFromVariables(widget, "tips", ctx), status: "direct", lumiaType: "loyaltyleaderboard" };
|
|
2057
|
+
if (t === "se-widget-top-cheerers-list") return { unit: mapLeaderboardFromVariables(widget, "cheers", ctx), status: "direct", lumiaType: "loyaltyleaderboard" };
|
|
2058
|
+
if (t === "se-widget-top-gifters-list") return { unit: mapLeaderboardFromVariables(widget, "gifts", ctx), status: "direct", lumiaType: "loyaltyleaderboard" };
|
|
2059
|
+
if (t === "se-widget-credit-roll") return { unit: mapCredits(widget, ctx), status: "partial", lumiaType: "credits" };
|
|
2060
|
+
if (t === "se-widget-twitch-chat") return { unit: mapChatbox(widget, ctx), status: "partial", lumiaType: "chatbox" };
|
|
2061
|
+
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" };
|
|
2062
|
+
if (t === "se-widget-countdown") return { unit: mapTimer(widget, ctx), status: "partial", lumiaType: "timer" };
|
|
2063
|
+
if (t === "se-widget-current-song") return { unit: mapNowPlaying(widget, ctx), status: "partial", lumiaType: "nowplaying" };
|
|
2064
|
+
if (t === "se-widget-kappagen") return { unit: mapKappagen(widget, ctx), status: "partial", lumiaType: "emotealert" };
|
|
2065
|
+
if (t === "se-widget-giveaway") return { unit: mapGiveaway(widget, ctx), status: "direct", lumiaType: "raffle" };
|
|
2066
|
+
if (t === "se-widget-media-share") return { unit: mapMediaShare(widget, ctx), status: "partial", lumiaType: "songrequest" };
|
|
2067
|
+
if (t === "se-widget-hype-cup") return { unit: mapHypeCup(widget, ctx), status: "partial", lumiaType: "tipjar" };
|
|
2068
|
+
if (t === "se-widget-bit-boss") return { unit: mapStreamBoss(widget, ctx), status: "partial", lumiaType: "streamboss" };
|
|
2069
|
+
if (t === "se-widget-hype-train") return { unit: mapHypetrain(widget, ctx), status: "partial", lumiaType: "hypetrain" };
|
|
2071
2070
|
if (t === "se-widget-custom-event-list" && isHypetrainCustomWidget(widget)) {
|
|
2072
|
-
return { unit: mapHypetrain(widget), status: "partial", lumiaType: "hypetrain" };
|
|
2071
|
+
return { unit: mapHypetrain(widget, ctx), status: "partial", lumiaType: "hypetrain" };
|
|
2073
2072
|
}
|
|
2074
|
-
if (t === "se-widget-custom-event-list") return { unit: mapCustom(widget), status: "partial", lumiaType: "custom" };
|
|
2075
|
-
if (t === "se-widget-snow") return { unit: mapSnow(widget), status: "direct", lumiaType: "video" };
|
|
2076
|
-
if (t === "se-widget-halloween-2019") return { unit: mapSlideshow(widget), status: "direct", lumiaType: "slideshow" };
|
|
2077
|
-
if (SEASONAL_IMAGE_TYPES.has(t)) return { unit: mapImage(widget), status: "direct", lumiaType: "image" };
|
|
2078
|
-
return { unit: mapUnsupportedAsText(widget), status: "placeholder", lumiaType: "text" };
|
|
2073
|
+
if (t === "se-widget-custom-event-list") return { unit: mapCustom(widget, ctx), status: "partial", lumiaType: "custom" };
|
|
2074
|
+
if (t === "se-widget-snow") return { unit: mapSnow(widget, ctx), status: "direct", lumiaType: "video" };
|
|
2075
|
+
if (t === "se-widget-halloween-2019") return { unit: mapSlideshow(widget, ctx), status: "direct", lumiaType: "slideshow" };
|
|
2076
|
+
if (SEASONAL_IMAGE_TYPES.has(t)) return { unit: mapImage(widget, ctx), status: "direct", lumiaType: "image" };
|
|
2077
|
+
return { unit: mapUnsupportedAsText(widget, ctx), status: "placeholder", lumiaType: "text" };
|
|
2079
2078
|
}
|
|
2080
2079
|
function recordCoverage(coverage, result, seType) {
|
|
2081
2080
|
const existing = coverage.mappings.find((m) => m.seType === seType);
|
|
@@ -2133,11 +2132,19 @@ function rewriteAssetURLs(overlay, mapping) {
|
|
|
2133
2132
|
settings: JSON.parse(rewritten)
|
|
2134
2133
|
};
|
|
2135
2134
|
}
|
|
2135
|
+
var MAX_MIRROR_ASSET_BYTES = 100 * 1024 * 1024;
|
|
2136
2136
|
async function mirrorOneAsset(url, upload, signal) {
|
|
2137
2137
|
const res = await fetch(url, { mode: "cors", signal });
|
|
2138
2138
|
if (!res.ok) throw new Error(`Source returned HTTP ${res.status}`);
|
|
2139
|
+
const declared = Number(res.headers.get("content-length"));
|
|
2140
|
+
if (Number.isFinite(declared) && declared > MAX_MIRROR_ASSET_BYTES) {
|
|
2141
|
+
throw new Error(`Asset is ${(declared / (1024 * 1024)).toFixed(1)} MB \u2014 over the ${MAX_MIRROR_ASSET_BYTES / (1024 * 1024)} MB import cap.`);
|
|
2142
|
+
}
|
|
2139
2143
|
const blob = await res.blob();
|
|
2140
2144
|
if (signal?.aborted) throw new DOMException("Aborted", "AbortError");
|
|
2145
|
+
if (blob.size > MAX_MIRROR_ASSET_BYTES) {
|
|
2146
|
+
throw new Error(`Asset is ${(blob.size / (1024 * 1024)).toFixed(1)} MB \u2014 over the ${MAX_MIRROR_ASSET_BYTES / (1024 * 1024)} MB import cap.`);
|
|
2147
|
+
}
|
|
2141
2148
|
const filename = filenameFromURL(url);
|
|
2142
2149
|
const type = blob.type || guessMime(filename);
|
|
2143
2150
|
const file = new File([blob], filename, { type });
|
|
@@ -4835,13 +4842,19 @@ function CustomOverlayPreview({
|
|
|
4835
4842
|
}) {
|
|
4836
4843
|
const srcdoc = useMemo5(() => {
|
|
4837
4844
|
const safeData = JSON.stringify(data ?? {});
|
|
4838
|
-
const safeJs =
|
|
4845
|
+
const safeJs = (js || "").replace(/<\/script>/gi, "<\\/script>");
|
|
4839
4846
|
const safeHtml = (html || "").replace(/<\/script>/gi, "<\\/script>");
|
|
4840
4847
|
const safeCss = (css || "").replace(/<\/style>/gi, "<\\/style>");
|
|
4841
4848
|
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>
|
|
4842
4849
|
window.DATA = ${safeData};
|
|
4843
4850
|
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() };
|
|
4844
|
-
|
|
4851
|
+
function __seImportShowError(e){document.body.insertAdjacentHTML('beforeend','<pre style="color:#fca5a5;font:11px monospace;padding:8px;">' + (e && e.message ? e.message : e) + '</pre>');}
|
|
4852
|
+
window.addEventListener('error', function(ev){__seImportShowError(ev.error || ev.message);});
|
|
4853
|
+
window.addEventListener('unhandledrejection', function(ev){__seImportShowError(ev.reason);});
|
|
4854
|
+
</script><script>
|
|
4855
|
+
(async function __seImportUserScript(){
|
|
4856
|
+
${safeJs}
|
|
4857
|
+
})().catch(__seImportShowError);
|
|
4845
4858
|
</script></body></html>`;
|
|
4846
4859
|
}, [html, css, js, data]);
|
|
4847
4860
|
return /* @__PURE__ */ jsx14(
|
|
@@ -5399,7 +5412,8 @@ function StepURL({
|
|
|
5399
5412
|
url,
|
|
5400
5413
|
setUrl,
|
|
5401
5414
|
error,
|
|
5402
|
-
busy
|
|
5415
|
+
busy,
|
|
5416
|
+
onSwitchToJwt
|
|
5403
5417
|
}) {
|
|
5404
5418
|
return /* @__PURE__ */ jsxs7("div", { className: panelClass("se-import-panel--narrow"), children: [
|
|
5405
5419
|
/* @__PURE__ */ jsx14(
|
|
@@ -5445,7 +5459,16 @@ function StepURL({
|
|
|
5445
5459
|
] })
|
|
5446
5460
|
] })
|
|
5447
5461
|
] }),
|
|
5448
|
-
error && /* @__PURE__ */ jsx14(ErrorPanel, { message: error.message, hint: error.hint })
|
|
5462
|
+
error && /* @__PURE__ */ jsx14(ErrorPanel, { message: error.message, hint: error.hint }),
|
|
5463
|
+
error?.editorUrl && onSwitchToJwt && /* @__PURE__ */ jsx14("div", { style: { marginTop: 12 }, children: /* @__PURE__ */ jsx14(
|
|
5464
|
+
LSButton,
|
|
5465
|
+
{
|
|
5466
|
+
type: "button",
|
|
5467
|
+
color: "primary",
|
|
5468
|
+
onClick: onSwitchToJwt,
|
|
5469
|
+
label: "Connect with JWT instead"
|
|
5470
|
+
}
|
|
5471
|
+
) })
|
|
5449
5472
|
] });
|
|
5450
5473
|
}
|
|
5451
5474
|
function StepDiscovery({
|
|
@@ -5952,10 +5975,7 @@ function SEImportWizard({
|
|
|
5952
5975
|
const mirrorStartedRef = useRef4(false);
|
|
5953
5976
|
const mirrorAbortRef = useRef4(null);
|
|
5954
5977
|
const filenameCacheRef = useRef4(/* @__PURE__ */ new Map());
|
|
5955
|
-
const assetUrls =
|
|
5956
|
-
() => result ? findSEAssetURLs(result.overlay) : [],
|
|
5957
|
-
[result]
|
|
5958
|
-
);
|
|
5978
|
+
const [assetUrls, setAssetUrls] = useState6([]);
|
|
5959
5979
|
const currentItem = originalReviewItems[reviewIndex];
|
|
5960
5980
|
const currentRow = currentItem ? rowStates[currentItem.moduleId] ?? { state: "pending" } : void 0;
|
|
5961
5981
|
const handleConnect = useCallback3(async () => {
|
|
@@ -6053,6 +6073,7 @@ function SEImportWizard({
|
|
|
6053
6073
|
}
|
|
6054
6074
|
const imported = importSEBootstrap(bootstrap);
|
|
6055
6075
|
setResult(imported);
|
|
6076
|
+
setAssetUrls(findSEAssetURLs(imported.overlay));
|
|
6056
6077
|
setOptions((o) => ({ ...o, name: imported.overlay.name }));
|
|
6057
6078
|
setOriginalReviewItems(imported.reviewItems);
|
|
6058
6079
|
setRowStates(
|
|
@@ -6068,9 +6089,10 @@ function SEImportWizard({
|
|
|
6068
6089
|
mirrorStartedRef.current = false;
|
|
6069
6090
|
setStep("discovery");
|
|
6070
6091
|
} catch (e) {
|
|
6092
|
+
const detail = e?.message ?? String(e);
|
|
6071
6093
|
setLoadError({
|
|
6072
|
-
message: "Could not load the overlay.",
|
|
6073
|
-
hint:
|
|
6094
|
+
message: detail || "Could not load the overlay.",
|
|
6095
|
+
hint: detail ? "If this looks transient, retry in a moment. Otherwise check the URL / token and try again." : void 0
|
|
6074
6096
|
});
|
|
6075
6097
|
} finally {
|
|
6076
6098
|
setLoading(false);
|
|
@@ -6101,7 +6123,8 @@ function SEImportWizard({
|
|
|
6101
6123
|
if (!parts.apikey) {
|
|
6102
6124
|
setLoadError({
|
|
6103
6125
|
message: "That looks like an editor URL, which doesn't include an access token.",
|
|
6104
|
-
hint: `Use the public preview URL (Overlay menu \u2192 Get URL \u2192 Public link). Overlay id: ${parts.overlayId}
|
|
6126
|
+
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}`,
|
|
6127
|
+
editorUrl: true
|
|
6105
6128
|
});
|
|
6106
6129
|
return;
|
|
6107
6130
|
}
|
|
@@ -6110,6 +6133,7 @@ function SEImportWizard({
|
|
|
6110
6133
|
const bootstrap = await fetchSEBootstrap(parts);
|
|
6111
6134
|
const imported = importSEBootstrap(bootstrap);
|
|
6112
6135
|
setResult(imported);
|
|
6136
|
+
setAssetUrls(findSEAssetURLs(imported.overlay));
|
|
6113
6137
|
setOptions((previous) => ({ ...previous, name: imported.overlay.name }));
|
|
6114
6138
|
setOriginalReviewItems(imported.reviewItems);
|
|
6115
6139
|
setRowStates(
|
|
@@ -6123,27 +6147,21 @@ function SEImportWizard({
|
|
|
6123
6147
|
setReviewIndex(0);
|
|
6124
6148
|
setStep("discovery");
|
|
6125
6149
|
} catch (error) {
|
|
6150
|
+
const detail = error?.message ?? String(error);
|
|
6126
6151
|
setLoadError({
|
|
6127
|
-
message: "Could not load the overlay.",
|
|
6128
|
-
hint:
|
|
6152
|
+
message: detail || "Could not load the overlay.",
|
|
6153
|
+
hint: detail ? "If this looks transient, retry in a moment. Otherwise check the URL / token and try again." : void 0
|
|
6129
6154
|
});
|
|
6130
6155
|
} finally {
|
|
6131
6156
|
setLoading(false);
|
|
6132
6157
|
}
|
|
6133
6158
|
}, [url]);
|
|
6159
|
+
const MIRROR_CONCURRENCY = 5;
|
|
6134
6160
|
const processAssetRow = useCallback3(
|
|
6135
6161
|
async (assetUrl, idx, existingByFilename, signal) => {
|
|
6136
6162
|
const filename = filenameFromURL(assetUrl);
|
|
6137
6163
|
const reuseUrl = existingByFilename.get(filename);
|
|
6138
6164
|
if (reuseUrl) {
|
|
6139
|
-
setResult(
|
|
6140
|
-
(current) => current ? {
|
|
6141
|
-
...current,
|
|
6142
|
-
overlay: rewriteAssetURLs(current.overlay, {
|
|
6143
|
-
[assetUrl]: reuseUrl
|
|
6144
|
-
})
|
|
6145
|
-
} : current
|
|
6146
|
-
);
|
|
6147
6165
|
return { state: "reused", newUrl: reuseUrl };
|
|
6148
6166
|
}
|
|
6149
6167
|
if (filename.toLowerCase().endsWith(".svg"))
|
|
@@ -6160,14 +6178,6 @@ function SEImportWizard({
|
|
|
6160
6178
|
signal
|
|
6161
6179
|
);
|
|
6162
6180
|
existingByFilename.set(filename, newUrl);
|
|
6163
|
-
setResult(
|
|
6164
|
-
(current) => current ? {
|
|
6165
|
-
...current,
|
|
6166
|
-
overlay: rewriteAssetURLs(current.overlay, {
|
|
6167
|
-
[assetUrl]: newUrl
|
|
6168
|
-
})
|
|
6169
|
-
} : current
|
|
6170
|
-
);
|
|
6171
6181
|
return { state: "done", newUrl };
|
|
6172
6182
|
} catch (error) {
|
|
6173
6183
|
if (error?.name === "AbortError" || signal?.aborted)
|
|
@@ -6177,8 +6187,15 @@ function SEImportWizard({
|
|
|
6177
6187
|
},
|
|
6178
6188
|
[uploadAsset]
|
|
6179
6189
|
);
|
|
6190
|
+
const applyRewrite = useCallback3((mapping) => {
|
|
6191
|
+
if (Object.keys(mapping).length === 0) return;
|
|
6192
|
+
setResult(
|
|
6193
|
+
(current) => current ? { ...current, overlay: rewriteAssetURLs(current.overlay, mapping) } : current
|
|
6194
|
+
);
|
|
6195
|
+
}, []);
|
|
6180
6196
|
const runMirror = useCallback3(async () => {
|
|
6181
6197
|
if (!result) return;
|
|
6198
|
+
mirrorAbortRef.current?.abort();
|
|
6182
6199
|
mirrorAbortRef.current = new AbortController();
|
|
6183
6200
|
const signal = mirrorAbortRef.current.signal;
|
|
6184
6201
|
setMirrorRunning(true);
|
|
@@ -6193,21 +6210,33 @@ function SEImportWizard({
|
|
|
6193
6210
|
if (name && asset.url) existingByFilename.set(name, asset.url);
|
|
6194
6211
|
}
|
|
6195
6212
|
filenameCacheRef.current = existingByFilename;
|
|
6196
|
-
|
|
6197
|
-
|
|
6198
|
-
|
|
6199
|
-
|
|
6200
|
-
|
|
6201
|
-
|
|
6202
|
-
|
|
6203
|
-
|
|
6204
|
-
|
|
6205
|
-
|
|
6206
|
-
|
|
6207
|
-
|
|
6208
|
-
|
|
6213
|
+
const rewriteMap = {};
|
|
6214
|
+
let cursor = 0;
|
|
6215
|
+
const worker = async () => {
|
|
6216
|
+
while (true) {
|
|
6217
|
+
if (signal.aborted) return;
|
|
6218
|
+
const i = cursor++;
|
|
6219
|
+
if (i >= initialRows.length) return;
|
|
6220
|
+
const outcome = await processAssetRow(
|
|
6221
|
+
initialRows[i].url,
|
|
6222
|
+
i,
|
|
6223
|
+
existingByFilename,
|
|
6224
|
+
signal
|
|
6225
|
+
);
|
|
6226
|
+
if (signal.aborted) return;
|
|
6227
|
+
if (outcome.newUrl && (outcome.state === "done" || outcome.state === "reused")) {
|
|
6228
|
+
rewriteMap[initialRows[i].url] = outcome.newUrl;
|
|
6229
|
+
}
|
|
6230
|
+
setMirrorRows(
|
|
6231
|
+
(previous) => previous.map((row, idx) => idx === i ? { ...row, ...outcome } : row)
|
|
6232
|
+
);
|
|
6233
|
+
}
|
|
6234
|
+
};
|
|
6235
|
+
const poolSize = Math.min(MIRROR_CONCURRENCY, initialRows.length);
|
|
6236
|
+
await Promise.all(Array.from({ length: poolSize }, worker));
|
|
6237
|
+
if (!signal.aborted) applyRewrite(rewriteMap);
|
|
6209
6238
|
setMirrorRunning(false);
|
|
6210
|
-
}, [assetUrls, existingAssets, processAssetRow, result]);
|
|
6239
|
+
}, [applyRewrite, assetUrls, existingAssets, processAssetRow, result]);
|
|
6211
6240
|
const retryMirrorRow = useCallback3(
|
|
6212
6241
|
async (idx) => {
|
|
6213
6242
|
const rowUrl = mirrorRows[idx]?.url;
|
|
@@ -6221,8 +6250,11 @@ function SEImportWizard({
|
|
|
6221
6250
|
setMirrorRows(
|
|
6222
6251
|
(previous) => previous.map((row, i) => i === idx ? { ...row, ...outcome } : row)
|
|
6223
6252
|
);
|
|
6253
|
+
if (outcome.newUrl && (outcome.state === "done" || outcome.state === "reused")) {
|
|
6254
|
+
applyRewrite({ [rowUrl]: outcome.newUrl });
|
|
6255
|
+
}
|
|
6224
6256
|
},
|
|
6225
|
-
[mirrorRows, processAssetRow]
|
|
6257
|
+
[applyRewrite, mirrorRows, processAssetRow]
|
|
6226
6258
|
);
|
|
6227
6259
|
useEffect6(() => {
|
|
6228
6260
|
if (step === "mirror" && !mirrorStartedRef.current) {
|
|
@@ -6230,6 +6262,13 @@ function SEImportWizard({
|
|
|
6230
6262
|
void runMirror();
|
|
6231
6263
|
}
|
|
6232
6264
|
}, [step, runMirror]);
|
|
6265
|
+
useEffect6(() => {
|
|
6266
|
+
return () => {
|
|
6267
|
+
setJwt("");
|
|
6268
|
+
setSeClient(null);
|
|
6269
|
+
mirrorAbortRef.current?.abort();
|
|
6270
|
+
};
|
|
6271
|
+
}, []);
|
|
6233
6272
|
useEffect6(() => {
|
|
6234
6273
|
if (step === "pick" && seClient && overlayList === null && !overlaysLoading) {
|
|
6235
6274
|
void loadOverlayList(seClient);
|
|
@@ -6624,7 +6663,12 @@ function SEImportWizard({
|
|
|
6624
6663
|
url,
|
|
6625
6664
|
setUrl,
|
|
6626
6665
|
error: loadError,
|
|
6627
|
-
busy: loading
|
|
6666
|
+
busy: loading,
|
|
6667
|
+
onSwitchToJwt: () => {
|
|
6668
|
+
setLoadError(null);
|
|
6669
|
+
setEntryMode("jwt");
|
|
6670
|
+
setStep("connect");
|
|
6671
|
+
}
|
|
6628
6672
|
}
|
|
6629
6673
|
),
|
|
6630
6674
|
step === "discovery" && result && /* @__PURE__ */ jsx14(StepDiscovery, { result, assetCount: assetUrls.length }),
|
|
@@ -6828,145 +6872,138 @@ function importSEBootstrap(bootstrap) {
|
|
|
6828
6872
|
name: seOverlay?.name,
|
|
6829
6873
|
canvas: { width: seOverlay?.settings?.width, height: seOverlay?.settings?.height },
|
|
6830
6874
|
widgetCount: widgets.length,
|
|
6831
|
-
widgetTypes: widgets.map((w) => w.type)
|
|
6832
|
-
fullBootstrap: bootstrap
|
|
6875
|
+
widgetTypes: widgets.map((w) => w.type)
|
|
6833
6876
|
});
|
|
6834
6877
|
const importCanvas = {
|
|
6835
6878
|
width: seOverlay?.settings?.width ?? 1920,
|
|
6836
6879
|
height: seOverlay?.settings?.height ?? 1080
|
|
6837
6880
|
};
|
|
6838
|
-
|
|
6881
|
+
const ctx = { canvas: importCanvas };
|
|
6839
6882
|
const layers = [];
|
|
6840
6883
|
const modules = {};
|
|
6841
6884
|
const reviewItems = [];
|
|
6842
|
-
|
|
6843
|
-
|
|
6844
|
-
|
|
6845
|
-
|
|
6846
|
-
|
|
6847
|
-
|
|
6848
|
-
|
|
6849
|
-
|
|
6850
|
-
|
|
6851
|
-
|
|
6852
|
-
|
|
6853
|
-
|
|
6854
|
-
|
|
6855
|
-
|
|
6856
|
-
|
|
6857
|
-
|
|
6858
|
-
|
|
6859
|
-
|
|
6860
|
-
|
|
6861
|
-
|
|
6862
|
-
|
|
6863
|
-
|
|
6864
|
-
|
|
6865
|
-
|
|
6866
|
-
|
|
6867
|
-
|
|
6868
|
-
|
|
6869
|
-
|
|
6870
|
-
|
|
6871
|
-
|
|
6872
|
-
|
|
6873
|
-
|
|
6874
|
-
|
|
6875
|
-
|
|
6876
|
-
|
|
6877
|
-
|
|
6878
|
-
expanded: w.variables?.expanded ?? true
|
|
6879
|
-
}
|
|
6880
|
-
};
|
|
6881
|
-
const module = {
|
|
6882
|
-
id: lumiaGroupId,
|
|
6883
|
-
loaded: true,
|
|
6884
|
-
settings: { title: defaultLabelForSEWidget(w), type: "group" },
|
|
6885
|
-
lights: [],
|
|
6886
|
-
css: {},
|
|
6887
|
-
content: {
|
|
6888
|
-
se_provenance: { widget: w, reason: "Imported as native group", status: "group" }
|
|
6889
|
-
},
|
|
6890
|
-
events: {},
|
|
6891
|
-
variables: {}
|
|
6892
|
-
};
|
|
6893
|
-
layers.push(layer);
|
|
6894
|
-
modules[lumiaGroupId] = module;
|
|
6895
|
-
recordCoverage(coverage, { unit: { layer, module }, status: "direct", lumiaType: "group" }, w.type);
|
|
6896
|
-
}
|
|
6897
|
-
for (const w of widgets) {
|
|
6898
|
-
if (w.type === "se-widget-group") continue;
|
|
6899
|
-
const result = dispatch(w);
|
|
6900
|
-
if (typeof w.group === "string") {
|
|
6901
|
-
const parentLumiaGroupId = seUidToLumiaGroupId[w.group];
|
|
6902
|
-
if (parentLumiaGroupId) {
|
|
6903
|
-
result.unit.layer.group = parentLumiaGroupId;
|
|
6904
|
-
}
|
|
6885
|
+
const seUidToLumiaGroupId = {};
|
|
6886
|
+
const groupWidgetByUid = {};
|
|
6887
|
+
for (const w of widgets) {
|
|
6888
|
+
if (w.type !== "se-widget-group") continue;
|
|
6889
|
+
const uid = w.variables?.uid;
|
|
6890
|
+
if (typeof uid !== "string") continue;
|
|
6891
|
+
const lumiaGroupId = nanoid3();
|
|
6892
|
+
seUidToLumiaGroupId[uid] = lumiaGroupId;
|
|
6893
|
+
groupWidgetByUid[uid] = w;
|
|
6894
|
+
}
|
|
6895
|
+
for (const uid of Object.keys(groupWidgetByUid)) {
|
|
6896
|
+
const w = groupWidgetByUid[uid];
|
|
6897
|
+
const lumiaGroupId = seUidToLumiaGroupId[uid];
|
|
6898
|
+
const parentUid = typeof w.group === "string" ? w.group : null;
|
|
6899
|
+
const parentLumiaGroupId = parentUid ? seUidToLumiaGroupId[parentUid] ?? null : null;
|
|
6900
|
+
const bounds = seCssToBounds(w.css, { width: 200, height: 200 }, importCanvas);
|
|
6901
|
+
const layer = {
|
|
6902
|
+
id: lumiaGroupId,
|
|
6903
|
+
type: "group",
|
|
6904
|
+
group: parentLumiaGroupId,
|
|
6905
|
+
bounds: {
|
|
6906
|
+
x: bounds.x,
|
|
6907
|
+
y: bounds.y,
|
|
6908
|
+
width: bounds.width,
|
|
6909
|
+
height: bounds.height,
|
|
6910
|
+
opacity: bounds.opacity,
|
|
6911
|
+
zIndex: bounds.zIndex,
|
|
6912
|
+
rotate: 0,
|
|
6913
|
+
scale: [1, 1],
|
|
6914
|
+
matrix: ""
|
|
6915
|
+
},
|
|
6916
|
+
state: {
|
|
6917
|
+
locked: w.locked ?? false,
|
|
6918
|
+
visible: w.visible ?? true,
|
|
6919
|
+
// SE's `variables.expanded` is the editor's collapsed/open state.
|
|
6920
|
+
expanded: w.variables?.expanded ?? true
|
|
6905
6921
|
}
|
|
6906
|
-
|
|
6907
|
-
|
|
6908
|
-
|
|
6909
|
-
|
|
6910
|
-
|
|
6911
|
-
|
|
6912
|
-
|
|
6913
|
-
|
|
6914
|
-
|
|
6915
|
-
|
|
6916
|
-
|
|
6917
|
-
|
|
6918
|
-
|
|
6919
|
-
|
|
6920
|
-
|
|
6921
|
-
|
|
6922
|
-
|
|
6923
|
-
|
|
6924
|
-
|
|
6922
|
+
};
|
|
6923
|
+
const module = {
|
|
6924
|
+
id: lumiaGroupId,
|
|
6925
|
+
loaded: true,
|
|
6926
|
+
settings: { title: defaultLabelForSEWidget(w), type: "group" },
|
|
6927
|
+
lights: [],
|
|
6928
|
+
css: {},
|
|
6929
|
+
content: {
|
|
6930
|
+
se_provenance: { widget: w, reason: "Imported as native group", status: "group" }
|
|
6931
|
+
},
|
|
6932
|
+
events: {},
|
|
6933
|
+
variables: {}
|
|
6934
|
+
};
|
|
6935
|
+
layers.push(layer);
|
|
6936
|
+
modules[lumiaGroupId] = module;
|
|
6937
|
+
recordCoverage(coverage, { unit: { layer, module }, status: "direct", lumiaType: "group" }, w.type);
|
|
6938
|
+
}
|
|
6939
|
+
for (const w of widgets) {
|
|
6940
|
+
if (w.type === "se-widget-group") continue;
|
|
6941
|
+
const result = dispatch(w, ctx);
|
|
6942
|
+
if (typeof w.group === "string") {
|
|
6943
|
+
const parentLumiaGroupId = seUidToLumiaGroupId[w.group];
|
|
6944
|
+
if (parentLumiaGroupId) {
|
|
6945
|
+
result.unit.layer.group = parentLumiaGroupId;
|
|
6925
6946
|
}
|
|
6926
|
-
modules[module.id] = module;
|
|
6927
|
-
recordCoverage(coverage, result, w.type);
|
|
6928
|
-
}
|
|
6929
|
-
if (coverage.mappings.some((m) => m.status === "placeholder")) {
|
|
6930
|
-
coverage.notes.push("Some widgets imported as placeholder text layers \u2014 see SE/05-widget-to-module-mapping.md for status.");
|
|
6931
|
-
}
|
|
6932
|
-
if (coverage.mappings.some((m) => m.seType === "se-widget-merch-goal")) {
|
|
6933
|
-
coverage.notes.push("Merch-goal widgets imported as generic goals \u2014 see SE/03-missing-variables.md \xA78 for the missing data source.");
|
|
6934
6947
|
}
|
|
6935
|
-
|
|
6936
|
-
|
|
6948
|
+
layers.push(result.unit.layer);
|
|
6949
|
+
const module = result.unit.module;
|
|
6950
|
+
if (result.status === "placeholder" || result.status === "template") {
|
|
6951
|
+
module.content = {
|
|
6952
|
+
...module.content ?? {},
|
|
6953
|
+
se_provenance: {
|
|
6954
|
+
widget: w,
|
|
6955
|
+
reason: reasonFor(w.type, result.status, result.flaggedOff),
|
|
6956
|
+
status: result.status,
|
|
6957
|
+
flaggedOff: result.flaggedOff
|
|
6958
|
+
}
|
|
6959
|
+
};
|
|
6960
|
+
reviewItems.push({
|
|
6961
|
+
moduleId: module.id,
|
|
6962
|
+
seWidget: w,
|
|
6963
|
+
status: result.status,
|
|
6964
|
+
reason: reasonFor(w.type, result.status, result.flaggedOff),
|
|
6965
|
+
flaggedOff: result.flaggedOff
|
|
6966
|
+
});
|
|
6937
6967
|
}
|
|
6938
|
-
|
|
6939
|
-
|
|
6940
|
-
|
|
6941
|
-
|
|
6942
|
-
|
|
6943
|
-
|
|
6944
|
-
|
|
6945
|
-
|
|
6946
|
-
|
|
6947
|
-
|
|
6948
|
-
|
|
6949
|
-
|
|
6950
|
-
|
|
6968
|
+
modules[module.id] = module;
|
|
6969
|
+
recordCoverage(coverage, result, w.type);
|
|
6970
|
+
}
|
|
6971
|
+
if (coverage.mappings.some((m) => m.status === "placeholder")) {
|
|
6972
|
+
coverage.notes.push("Some widgets imported as placeholder text layers \u2014 see SE/05-widget-to-module-mapping.md for status.");
|
|
6973
|
+
}
|
|
6974
|
+
if (coverage.mappings.some((m) => m.seType === "se-widget-merch-goal")) {
|
|
6975
|
+
coverage.notes.push("Merch-goal widgets imported as generic goals \u2014 see SE/03-missing-variables.md \xA78 for the missing data source.");
|
|
6976
|
+
}
|
|
6977
|
+
if (coverage.mappings.some((m) => m.seType === "se-widget-custom-event-list")) {
|
|
6978
|
+
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.");
|
|
6979
|
+
}
|
|
6980
|
+
const overlay = {
|
|
6981
|
+
uuid: "",
|
|
6982
|
+
listen_id: "",
|
|
6983
|
+
name: seOverlay?.name ? `[SE] ${seOverlay.name}` : `Imported from StreamElements (${seOverlay?._id ?? ""})`,
|
|
6984
|
+
description: seOverlay?._id ? `Imported from StreamElements overlay ${seOverlay._id}` : "Imported from StreamElements",
|
|
6985
|
+
settings: {
|
|
6951
6986
|
metadata: {
|
|
6952
|
-
width: seOverlay?.settings?.width ??
|
|
6953
|
-
height: seOverlay?.settings?.height ??
|
|
6987
|
+
width: seOverlay?.settings?.width ?? 1920,
|
|
6988
|
+
height: seOverlay?.settings?.height ?? 1080
|
|
6954
6989
|
},
|
|
6955
|
-
|
|
6956
|
-
|
|
6957
|
-
}
|
|
6958
|
-
|
|
6959
|
-
|
|
6960
|
-
|
|
6961
|
-
|
|
6962
|
-
|
|
6963
|
-
|
|
6964
|
-
|
|
6965
|
-
|
|
6966
|
-
|
|
6967
|
-
|
|
6968
|
-
|
|
6969
|
-
|
|
6990
|
+
layers,
|
|
6991
|
+
modules
|
|
6992
|
+
},
|
|
6993
|
+
metadata: {
|
|
6994
|
+
width: seOverlay?.settings?.width ?? null,
|
|
6995
|
+
height: seOverlay?.settings?.height ?? null
|
|
6996
|
+
},
|
|
6997
|
+
layers_count: layers.length,
|
|
6998
|
+
hasFullSettings: true
|
|
6999
|
+
};
|
|
7000
|
+
console.log("[SE-IMPORT] import complete", {
|
|
7001
|
+
overlayName: overlay.name,
|
|
7002
|
+
layerCount: overlay.layers_count,
|
|
7003
|
+
coverage,
|
|
7004
|
+
reviewItemCount: reviewItems.length
|
|
7005
|
+
});
|
|
7006
|
+
return { overlay, coverage, reviewItems };
|
|
6970
7007
|
}
|
|
6971
7008
|
function applyReviewAction(result, moduleId, action, payload) {
|
|
6972
7009
|
if (action === "keep") return result;
|
|
@@ -6995,9 +7032,15 @@ function applyReviewAction(result, moduleId, action, payload) {
|
|
|
6995
7032
|
state: existingLayer.state
|
|
6996
7033
|
};
|
|
6997
7034
|
const provenance2 = existing2.content?.se_provenance;
|
|
7035
|
+
const mergedLights = (existing2.lights?.length ?? 0) > 0 ? existing2.lights : transplant.module.lights;
|
|
7036
|
+
const mergedEvents = existing2.events && Object.keys(existing2.events).length > 0 ? { ...transplant.module.events, ...existing2.events } : transplant.module.events;
|
|
7037
|
+
const mergedVariables = existing2.variables && Object.keys(existing2.variables).length > 0 ? { ...transplant.module.variables, ...existing2.variables } : transplant.module.variables;
|
|
6998
7038
|
const newModule = {
|
|
6999
7039
|
...transplant.module,
|
|
7000
7040
|
id: moduleId,
|
|
7041
|
+
lights: mergedLights,
|
|
7042
|
+
events: mergedEvents,
|
|
7043
|
+
variables: mergedVariables,
|
|
7001
7044
|
content: { ...transplant.module.content ?? {}, se_provenance: provenance2 }
|
|
7002
7045
|
};
|
|
7003
7046
|
const rootOldNewId = transplant.layer.id;
|
|
@@ -7068,6 +7111,11 @@ function buildAIPromptForSEWidget(widget, canvas) {
|
|
|
7068
7111
|
const listener = typeof widget.listener === "string" ? widget.listener : null;
|
|
7069
7112
|
const listeners = widget.listeners && typeof widget.listeners === "object" && !Array.isArray(widget.listeners) ? Object.keys(widget.listeners) : null;
|
|
7070
7113
|
const landing = getAILandingBounds(widget, canvas);
|
|
7114
|
+
const VARIABLES_BUDGET = 6e3;
|
|
7115
|
+
const fullVariables = JSON.stringify(variables, null, 2);
|
|
7116
|
+
const truncated = fullVariables.length > VARIABLES_BUDGET;
|
|
7117
|
+
const variablesJson = truncated ? `${fullVariables.slice(0, VARIABLES_BUDGET)}
|
|
7118
|
+
\u2026[truncated \u2014 ${fullVariables.length - VARIABLES_BUDGET} more bytes elided]` : fullVariables;
|
|
7071
7119
|
return [
|
|
7072
7120
|
"Recreate a StreamElements overlay widget as a Lumia Stream custom overlay (HTML + CSS + JS).",
|
|
7073
7121
|
"",
|
|
@@ -7075,10 +7123,11 @@ function buildAIPromptForSEWidget(widget, canvas) {
|
|
|
7075
7123
|
listener ? `Primary event listener: ${listener}` : "",
|
|
7076
7124
|
listeners ? `Subscribes to events: ${listeners.join(", ")}` : "",
|
|
7077
7125
|
`Canvas: ${landing.width} \xD7 ${landing.height}`,
|
|
7126
|
+
truncated ? "Note: the widget config below is truncated \u2014 focus on visible/structural fields and ignore tail content you cannot see." : "",
|
|
7078
7127
|
"",
|
|
7079
7128
|
"Widget configuration (JSON):",
|
|
7080
7129
|
"```json",
|
|
7081
|
-
|
|
7130
|
+
variablesJson,
|
|
7082
7131
|
"```",
|
|
7083
7132
|
"",
|
|
7084
7133
|
"Constraints \u2014 your output must run inside a Lumia Custom Overlay sandboxed iframe:",
|
|
@@ -7098,7 +7147,6 @@ export {
|
|
|
7098
7147
|
SEAuthError,
|
|
7099
7148
|
SEClient,
|
|
7100
7149
|
SEImportWizard,
|
|
7101
|
-
SE_IMPORT_FLAGS,
|
|
7102
7150
|
SE_WIDGET_TO_MARKETPLACE_CANDIDATES,
|
|
7103
7151
|
applyReviewAction,
|
|
7104
7152
|
buildAIPromptForSEWidget,
|