@lumiastream/ui 0.5.3 → 0.5.4
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/LSButton.js +1 -1
- package/dist/LSCheckbox.js +1 -1
- package/dist/LSColorPicker.js +1 -1
- package/dist/LSDatePicker.js +1 -1
- package/dist/LSFontPicker.js +1 -1
- package/dist/LSInput.js +1 -1
- package/dist/LSMultiSelect.js +1 -1
- package/dist/LSRadio.js +1 -1
- package/dist/LSSelect.js +1 -1
- package/dist/LSSliderInput.js +1 -1
- package/dist/LSTextField.js +1 -1
- package/dist/LSVariableInputField.js +1 -1
- package/dist/components.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +837 -456
- package/dist/se-import.d.ts +9 -3
- package/dist/se-import.js +836 -455
- package/package.json +1 -1
package/dist/se-import.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/se-import/index.ts
|
|
2
|
-
import { nanoid as
|
|
2
|
+
import { nanoid as nanoid8 } from "nanoid";
|
|
3
3
|
|
|
4
4
|
// src/se-import/translate.ts
|
|
5
5
|
var SE_TO_LUMIA_PLACEHOLDER = {
|
|
@@ -1064,12 +1064,26 @@ function mapImage(widget, ctx) {
|
|
|
1064
1064
|
);
|
|
1065
1065
|
const innerWidth = widget.image?.css?.width;
|
|
1066
1066
|
const innerHeight = widget.image?.css?.height;
|
|
1067
|
+
const outerWidthExplicit = isExplicitPxSize(widget.css?.width);
|
|
1068
|
+
const outerHeightExplicit = isExplicitPxSize(widget.css?.height);
|
|
1067
1069
|
const bounds = unit.layer.bounds;
|
|
1068
|
-
if (innerWidth === "auto") bounds.autoWidth = true;
|
|
1069
|
-
if (innerHeight === "auto") bounds.autoHeight = true;
|
|
1070
|
+
if (innerWidth === "auto" && !outerWidthExplicit) bounds.autoWidth = true;
|
|
1071
|
+
if (innerHeight === "auto" && !outerHeightExplicit) bounds.autoHeight = true;
|
|
1070
1072
|
return unit;
|
|
1071
1073
|
}
|
|
1074
|
+
function isExplicitPxSize(value) {
|
|
1075
|
+
if (value == null) return false;
|
|
1076
|
+
if (typeof value === "number") return Number.isFinite(value) && value > 0;
|
|
1077
|
+
if (typeof value !== "string") return false;
|
|
1078
|
+
const s = value.trim().toLowerCase();
|
|
1079
|
+
if (s === "" || s === "auto") return false;
|
|
1080
|
+
if (s.endsWith("%")) return false;
|
|
1081
|
+
const parsed = parseFloat(s);
|
|
1082
|
+
return Number.isFinite(parsed) && parsed > 0;
|
|
1083
|
+
}
|
|
1072
1084
|
function mapVideo(widget, ctx) {
|
|
1085
|
+
const innerWidth = widget.video?.css?.width;
|
|
1086
|
+
const isInnerAutoWidth = innerWidth === "auto";
|
|
1073
1087
|
const unit = buildUnit(
|
|
1074
1088
|
widget,
|
|
1075
1089
|
"video",
|
|
@@ -1078,12 +1092,19 @@ function mapVideo(widget, ctx) {
|
|
|
1078
1092
|
src: widget.video?.src ?? "",
|
|
1079
1093
|
volume: widget.video?.volume ?? 0.5,
|
|
1080
1094
|
loop: true,
|
|
1081
|
-
muted: false
|
|
1095
|
+
muted: false,
|
|
1096
|
+
...isInnerAutoWidth ? { objectFit: "contain" } : {}
|
|
1082
1097
|
}
|
|
1083
1098
|
},
|
|
1084
1099
|
ctx
|
|
1085
1100
|
);
|
|
1086
|
-
unit.layer.bounds
|
|
1101
|
+
const bounds = unit.layer.bounds;
|
|
1102
|
+
if (isInnerAutoWidth) {
|
|
1103
|
+
bounds.autoWidth = true;
|
|
1104
|
+
bounds.autoHeight = true;
|
|
1105
|
+
} else {
|
|
1106
|
+
bounds.autoHeight = true;
|
|
1107
|
+
}
|
|
1087
1108
|
return unit;
|
|
1088
1109
|
}
|
|
1089
1110
|
var LUMIA_SNOW_WEBM = "https://storage.lumiastream.com/overlays/global/seasonal/snow.webm";
|
|
@@ -1142,118 +1163,157 @@ function mapReadout(widget, fallbackVar, ctx) {
|
|
|
1142
1163
|
|
|
1143
1164
|
// src/se-import/mappers/alert.ts
|
|
1144
1165
|
import { LumiaAlertConfigs } from "@lumiastream/lumia-types";
|
|
1166
|
+
import { nanoid as nanoid2 } from "nanoid";
|
|
1167
|
+
|
|
1168
|
+
// src/se-import/import-utils.ts
|
|
1169
|
+
function pxToNumber(raw, fallback) {
|
|
1170
|
+
if (typeof raw === "number" && Number.isFinite(raw)) return raw;
|
|
1171
|
+
if (typeof raw !== "string") return fallback;
|
|
1172
|
+
const trimmed = raw.trim();
|
|
1173
|
+
if (!trimmed) return fallback;
|
|
1174
|
+
if (trimmed.endsWith("px")) {
|
|
1175
|
+
const n2 = parseFloat(trimmed.slice(0, -2));
|
|
1176
|
+
return Number.isFinite(n2) ? n2 : fallback;
|
|
1177
|
+
}
|
|
1178
|
+
const n = parseFloat(trimmed);
|
|
1179
|
+
return Number.isFinite(n) ? n : fallback;
|
|
1180
|
+
}
|
|
1181
|
+
function parseDurationMs(raw, fallbackMs) {
|
|
1182
|
+
if (typeof raw === "number" && Number.isFinite(raw)) return raw;
|
|
1183
|
+
if (typeof raw !== "string") return fallbackMs;
|
|
1184
|
+
const trimmed = raw.trim();
|
|
1185
|
+
if (!trimmed) return fallbackMs;
|
|
1186
|
+
if (trimmed.endsWith("ms")) {
|
|
1187
|
+
const n2 = parseFloat(trimmed.slice(0, -2));
|
|
1188
|
+
return Number.isFinite(n2) ? n2 : fallbackMs;
|
|
1189
|
+
}
|
|
1190
|
+
if (trimmed.endsWith("s")) {
|
|
1191
|
+
const n2 = parseFloat(trimmed.slice(0, -1));
|
|
1192
|
+
return Number.isFinite(n2) ? n2 * 1e3 : fallbackMs;
|
|
1193
|
+
}
|
|
1194
|
+
const n = parseFloat(trimmed);
|
|
1195
|
+
return Number.isFinite(n) ? n : fallbackMs;
|
|
1196
|
+
}
|
|
1197
|
+
function buildImportDescription(head, sourceUrl) {
|
|
1198
|
+
if (typeof sourceUrl === "string" && sourceUrl.trim().length > 0) {
|
|
1199
|
+
return `${head}
|
|
1200
|
+
Source: ${sourceUrl.trim()}`;
|
|
1201
|
+
}
|
|
1202
|
+
return head;
|
|
1203
|
+
}
|
|
1145
1204
|
|
|
1146
1205
|
// src/se-import/mappers/provider-alerts.ts
|
|
1206
|
+
import { LumiaAlertValues } from "@lumiastream/lumia-types";
|
|
1147
1207
|
var ALERT_BOX_KEYS = {
|
|
1148
1208
|
twitch: {
|
|
1149
|
-
follower: [
|
|
1150
|
-
subscriber: [
|
|
1151
|
-
tip: [
|
|
1152
|
-
cheer: [
|
|
1153
|
-
raid: [
|
|
1154
|
-
merch: [
|
|
1155
|
-
purchase: [
|
|
1156
|
-
charityCampaignDonation: [
|
|
1209
|
+
follower: [LumiaAlertValues.TWITCH_FOLLOWER],
|
|
1210
|
+
subscriber: [LumiaAlertValues.TWITCH_SUBSCRIBER],
|
|
1211
|
+
tip: [LumiaAlertValues.LUMIASTREAM_DONATION, LumiaAlertValues.STREAMELEMENTS_DONATION],
|
|
1212
|
+
cheer: [LumiaAlertValues.TWITCH_BITS],
|
|
1213
|
+
raid: [LumiaAlertValues.TWITCH_RAID],
|
|
1214
|
+
merch: [LumiaAlertValues.FOURTHWALL_SHOPORDER],
|
|
1215
|
+
purchase: [LumiaAlertValues.FOURTHWALL_SHOPORDER],
|
|
1216
|
+
charityCampaignDonation: [LumiaAlertValues.TWITCH_CHARITY_DONATION]
|
|
1157
1217
|
},
|
|
1158
1218
|
kick: {
|
|
1159
|
-
follower: [
|
|
1160
|
-
subscriber: [
|
|
1161
|
-
tip: [
|
|
1162
|
-
cheer: [
|
|
1163
|
-
raid: [
|
|
1164
|
-
host: [
|
|
1165
|
-
merch: [
|
|
1166
|
-
purchase: [
|
|
1219
|
+
follower: [LumiaAlertValues.KICK_FOLLOWER],
|
|
1220
|
+
subscriber: [LumiaAlertValues.KICK_SUBSCRIBER],
|
|
1221
|
+
tip: [LumiaAlertValues.LUMIASTREAM_DONATION, LumiaAlertValues.STREAMELEMENTS_DONATION],
|
|
1222
|
+
cheer: [LumiaAlertValues.KICK_KICKS],
|
|
1223
|
+
raid: [LumiaAlertValues.KICK_HOST],
|
|
1224
|
+
host: [LumiaAlertValues.KICK_HOST],
|
|
1225
|
+
merch: [LumiaAlertValues.FOURTHWALL_SHOPORDER],
|
|
1226
|
+
purchase: [LumiaAlertValues.FOURTHWALL_SHOPORDER]
|
|
1167
1227
|
},
|
|
1168
1228
|
youtube: {
|
|
1169
|
-
follower: [
|
|
1170
|
-
subscriber: [
|
|
1171
|
-
tip: [
|
|
1172
|
-
cheer: [
|
|
1173
|
-
merch: [
|
|
1174
|
-
purchase: [
|
|
1229
|
+
follower: [LumiaAlertValues.YOUTUBE_SUBSCRIBER],
|
|
1230
|
+
subscriber: [LumiaAlertValues.YOUTUBE_MEMBER],
|
|
1231
|
+
tip: [LumiaAlertValues.LUMIASTREAM_DONATION, LumiaAlertValues.STREAMELEMENTS_DONATION],
|
|
1232
|
+
cheer: [LumiaAlertValues.YOUTUBE_SUPERCHAT],
|
|
1233
|
+
merch: [LumiaAlertValues.FOURTHWALL_SHOPORDER],
|
|
1234
|
+
purchase: [LumiaAlertValues.FOURTHWALL_SHOPORDER]
|
|
1175
1235
|
}
|
|
1176
1236
|
};
|
|
1177
1237
|
var KAPPAGEN_KEYS = {
|
|
1178
1238
|
twitch: {
|
|
1179
|
-
follower:
|
|
1180
|
-
subscriber:
|
|
1181
|
-
cheer:
|
|
1182
|
-
raid:
|
|
1183
|
-
tip:
|
|
1239
|
+
follower: LumiaAlertValues.TWITCH_FOLLOWER,
|
|
1240
|
+
subscriber: LumiaAlertValues.TWITCH_SUBSCRIBER,
|
|
1241
|
+
cheer: LumiaAlertValues.TWITCH_BITS,
|
|
1242
|
+
raid: LumiaAlertValues.TWITCH_RAID,
|
|
1243
|
+
tip: LumiaAlertValues.LUMIASTREAM_DONATION
|
|
1184
1244
|
},
|
|
1185
1245
|
kick: {
|
|
1186
|
-
follower:
|
|
1187
|
-
subscriber:
|
|
1188
|
-
cheer:
|
|
1189
|
-
raid:
|
|
1190
|
-
tip:
|
|
1246
|
+
follower: LumiaAlertValues.KICK_FOLLOWER,
|
|
1247
|
+
subscriber: LumiaAlertValues.KICK_SUBSCRIBER,
|
|
1248
|
+
cheer: LumiaAlertValues.KICK_KICKS,
|
|
1249
|
+
raid: LumiaAlertValues.KICK_HOST,
|
|
1250
|
+
tip: LumiaAlertValues.LUMIASTREAM_DONATION
|
|
1191
1251
|
},
|
|
1192
1252
|
youtube: {
|
|
1193
|
-
follower:
|
|
1194
|
-
subscriber:
|
|
1195
|
-
cheer:
|
|
1196
|
-
tip:
|
|
1253
|
+
follower: LumiaAlertValues.YOUTUBE_SUBSCRIBER,
|
|
1254
|
+
subscriber: LumiaAlertValues.YOUTUBE_MEMBER,
|
|
1255
|
+
cheer: LumiaAlertValues.YOUTUBE_SUPERCHAT,
|
|
1256
|
+
tip: LumiaAlertValues.LUMIASTREAM_DONATION
|
|
1197
1257
|
}
|
|
1198
1258
|
};
|
|
1199
1259
|
var STREAMBOSS_LISTENER_KEYS = {
|
|
1200
1260
|
twitch: {
|
|
1201
|
-
"follower-latest": [
|
|
1202
|
-
"subscriber-latest": [
|
|
1203
|
-
"tip-latest": [
|
|
1204
|
-
"cheer-latest": [
|
|
1205
|
-
"raid-latest": [
|
|
1261
|
+
"follower-latest": [LumiaAlertValues.TWITCH_FOLLOWER],
|
|
1262
|
+
"subscriber-latest": [LumiaAlertValues.TWITCH_SUBSCRIBER],
|
|
1263
|
+
"tip-latest": [LumiaAlertValues.LUMIASTREAM_DONATION, LumiaAlertValues.STREAMELEMENTS_DONATION],
|
|
1264
|
+
"cheer-latest": [LumiaAlertValues.TWITCH_BITS],
|
|
1265
|
+
"raid-latest": [LumiaAlertValues.TWITCH_RAID]
|
|
1206
1266
|
},
|
|
1207
1267
|
kick: {
|
|
1208
|
-
"follower-latest": [
|
|
1209
|
-
"subscriber-latest": [
|
|
1210
|
-
"tip-latest": [
|
|
1211
|
-
"cheer-latest": [
|
|
1212
|
-
"raid-latest": [
|
|
1268
|
+
"follower-latest": [LumiaAlertValues.KICK_FOLLOWER],
|
|
1269
|
+
"subscriber-latest": [LumiaAlertValues.KICK_SUBSCRIBER],
|
|
1270
|
+
"tip-latest": [LumiaAlertValues.LUMIASTREAM_DONATION, LumiaAlertValues.STREAMELEMENTS_DONATION],
|
|
1271
|
+
"cheer-latest": [LumiaAlertValues.KICK_KICKS],
|
|
1272
|
+
"raid-latest": [LumiaAlertValues.KICK_HOST]
|
|
1213
1273
|
},
|
|
1214
1274
|
youtube: {
|
|
1215
|
-
"follower-latest": [
|
|
1216
|
-
"subscriber-latest": [
|
|
1217
|
-
"tip-latest": [
|
|
1218
|
-
"cheer-latest": [
|
|
1275
|
+
"follower-latest": [LumiaAlertValues.YOUTUBE_SUBSCRIBER],
|
|
1276
|
+
"subscriber-latest": [LumiaAlertValues.YOUTUBE_MEMBER],
|
|
1277
|
+
"tip-latest": [LumiaAlertValues.LUMIASTREAM_DONATION, LumiaAlertValues.STREAMELEMENTS_DONATION],
|
|
1278
|
+
"cheer-latest": [LumiaAlertValues.YOUTUBE_SUPERCHAT]
|
|
1219
1279
|
}
|
|
1220
1280
|
};
|
|
1221
1281
|
var CURATED_ALERTS = {
|
|
1222
1282
|
twitch: [
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1283
|
+
LumiaAlertValues.TWITCH_FOLLOWER,
|
|
1284
|
+
LumiaAlertValues.TWITCH_SUBSCRIBER,
|
|
1285
|
+
LumiaAlertValues.TWITCH_GIFT_SUBSCRIPTION,
|
|
1286
|
+
LumiaAlertValues.TWITCH_BITS,
|
|
1287
|
+
LumiaAlertValues.TWITCH_BITS_COMBO,
|
|
1288
|
+
LumiaAlertValues.TWITCH_RAID,
|
|
1289
|
+
LumiaAlertValues.TWITCH_POINTS,
|
|
1290
|
+
LumiaAlertValues.TWITCH_REDEMPTION,
|
|
1291
|
+
LumiaAlertValues.TWITCH_CHARITY_DONATION,
|
|
1292
|
+
LumiaAlertValues.TWITCH_HYPETRAIN_STARTED,
|
|
1293
|
+
LumiaAlertValues.TWITCH_CLIP,
|
|
1294
|
+
LumiaAlertValues.TWITCH_SHOUTOUT_RECEIVE,
|
|
1295
|
+
LumiaAlertValues.TWITCH_WATCH_STREAK,
|
|
1296
|
+
LumiaAlertValues.TWITCH_FIRST_CHATTER,
|
|
1297
|
+
LumiaAlertValues.TWITCH_EXTENSION
|
|
1238
1298
|
],
|
|
1239
1299
|
kick: [
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1300
|
+
LumiaAlertValues.KICK_FOLLOWER,
|
|
1301
|
+
LumiaAlertValues.KICK_SUBSCRIBER,
|
|
1302
|
+
LumiaAlertValues.KICK_GIFT_SUBSCRIPTION,
|
|
1303
|
+
LumiaAlertValues.KICK_KICKS,
|
|
1304
|
+
LumiaAlertValues.KICK_HOST,
|
|
1305
|
+
LumiaAlertValues.KICK_POINTS,
|
|
1306
|
+
LumiaAlertValues.KICK_FIRST_CHATTER
|
|
1247
1307
|
],
|
|
1248
1308
|
youtube: [
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1309
|
+
LumiaAlertValues.YOUTUBE_SUBSCRIBER,
|
|
1310
|
+
LumiaAlertValues.YOUTUBE_MEMBER,
|
|
1311
|
+
LumiaAlertValues.YOUTUBE_GIFT_MEMBERS,
|
|
1312
|
+
LumiaAlertValues.YOUTUBE_SUPERCHAT,
|
|
1313
|
+
LumiaAlertValues.YOUTUBE_SUPERSTICKER,
|
|
1314
|
+
LumiaAlertValues.YOUTUBE_GIFTS,
|
|
1315
|
+
LumiaAlertValues.YOUTUBE_LIKE,
|
|
1316
|
+
LumiaAlertValues.YOUTUBE_FIRST_CHATTER
|
|
1257
1317
|
]
|
|
1258
1318
|
};
|
|
1259
1319
|
function curatedAlertsFor(provider = "twitch") {
|
|
@@ -1321,8 +1381,12 @@ function buildAlertEvent(seEvent, seEventType) {
|
|
|
1321
1381
|
const shadowEnabled = text.enableShadow !== false;
|
|
1322
1382
|
const importedTextShadow = css["text-shadow"] ?? "rgba(0, 0, 0, 0.8) 1px 1px 1px";
|
|
1323
1383
|
const textShadow = shadowEnabled ? importedTextShadow : "none";
|
|
1324
|
-
const
|
|
1325
|
-
const pushTextUp =
|
|
1384
|
+
const allowPush = seLayoutUsesPushText(seEvent.layout);
|
|
1385
|
+
const pushTextUp = allowPush ? pxToNumber(css["margin-top"], 0) : 0;
|
|
1386
|
+
const pushTextLeft = allowPush ? pxToNumber(css["margin-left"], 0) : 0;
|
|
1387
|
+
const usesColumnLayout = !seEvent.layout || seEvent.layout === "column";
|
|
1388
|
+
const usesRowLayout = seEvent.layout === "row";
|
|
1389
|
+
const tightLayout = allowPush && (usesColumnLayout || usesRowLayout);
|
|
1326
1390
|
const variations = (seEvent.variations ?? []).map((variation) => buildAlertVariation(variation, seEventType, seEvent)).filter((v) => v != null);
|
|
1327
1391
|
const defaultDuration = (seEventType && SE_DEFAULT_DURATION_BY_EVENT[seEventType]) ?? 8;
|
|
1328
1392
|
return {
|
|
@@ -1349,7 +1413,7 @@ function buildAlertEvent(seEvent, seEventType) {
|
|
|
1349
1413
|
showBrandIcon: true
|
|
1350
1414
|
},
|
|
1351
1415
|
settings: { duration: (seEvent.duration ?? defaultDuration) * 1e3 },
|
|
1352
|
-
layout: { value: translateLayout(seEvent.layout), css: {}, customCss: "", pushTextUp, pushTextLeft:
|
|
1416
|
+
layout: { value: translateLayout(seEvent.layout), css: {}, customCss: "", pushTextUp, pushTextLeft, tight: tightLayout },
|
|
1353
1417
|
text: {
|
|
1354
1418
|
css: {
|
|
1355
1419
|
fontFamily: css["font-family"] ?? "Roboto",
|
|
@@ -1358,6 +1422,10 @@ function buildAlertEvent(seEvent, seEventType) {
|
|
|
1358
1422
|
fontWeight: css["font-weight"] ?? "bold",
|
|
1359
1423
|
textShadow,
|
|
1360
1424
|
textAlign: css["text-align"] ?? "center"
|
|
1425
|
+
// SE's per-event `margin-top` / `margin-left` are routed to
|
|
1426
|
+
// `layout.pushTextUp` / `layout.pushTextLeft` above (not
|
|
1427
|
+
// stamped on `text.css`) so they land in the alert editor's
|
|
1428
|
+
// dedicated sliders. See the long comment near `pushTextUp`.
|
|
1361
1429
|
},
|
|
1362
1430
|
messageCss,
|
|
1363
1431
|
content: { value: translateSeText(text.message ?? "") }
|
|
@@ -1427,6 +1495,15 @@ function buildAlertVariation(v, seEventType, parentSettings) {
|
|
|
1427
1495
|
const mergedSettings = deepMergeSeSettings(parentSettings ? parentInheritable : void 0, v.settings);
|
|
1428
1496
|
const base = buildAlertEvent(mergedSettings, seEventType);
|
|
1429
1497
|
return {
|
|
1498
|
+
// Stable per-variation ID. Lumia's alert editor (Settings.tsx) auto-
|
|
1499
|
+
// stamps a nanoid on first open for any variation missing an id, but
|
|
1500
|
+
// that leaves a window where the imported overlay's variations have
|
|
1501
|
+
// no stable key — list components rendering them key on
|
|
1502
|
+
// `variation.id || variation.name || idx` and adjacent variations
|
|
1503
|
+
// with the same name collide. Stamping at import time gives every
|
|
1504
|
+
// variation a stable identity from save #1 without waiting for the
|
|
1505
|
+
// editor's first mount.
|
|
1506
|
+
id: nanoid2(),
|
|
1430
1507
|
name: v.name ?? "Variation",
|
|
1431
1508
|
on: v.enabled !== false,
|
|
1432
1509
|
randomChance: v.chance ?? 100,
|
|
@@ -1510,7 +1587,7 @@ function mapSingleAlert(widget, lumiaAlertKey, ctx) {
|
|
|
1510
1587
|
css: { ...topCss, ...innerCss }
|
|
1511
1588
|
};
|
|
1512
1589
|
}
|
|
1513
|
-
const built = buildAlertEvent(merged);
|
|
1590
|
+
const built = buildAlertEvent(merged, void 0);
|
|
1514
1591
|
const events = {
|
|
1515
1592
|
[lumiaAlertKey]: built
|
|
1516
1593
|
};
|
|
@@ -1656,7 +1733,7 @@ function mapGoal(widget, ctx) {
|
|
|
1656
1733
|
}
|
|
1657
1734
|
|
|
1658
1735
|
// src/se-import/mappers/custom.ts
|
|
1659
|
-
import { nanoid as
|
|
1736
|
+
import { nanoid as nanoid3 } from "nanoid";
|
|
1660
1737
|
var SOURCE_TO_LUMIA_FIELD_TYPE = {
|
|
1661
1738
|
text: "input",
|
|
1662
1739
|
number: "number",
|
|
@@ -1746,7 +1823,7 @@ function mapCustom(widget, ctx) {
|
|
|
1746
1823
|
// imports — codeId is the scoping key for variables, storage, and
|
|
1747
1824
|
// event listeners. Mint a fresh nanoid so every imported widget
|
|
1748
1825
|
// has its own scope.
|
|
1749
|
-
codeId:
|
|
1826
|
+
codeId: nanoid3(),
|
|
1750
1827
|
html: v.html ?? "",
|
|
1751
1828
|
css: v.css ?? "",
|
|
1752
1829
|
js: v.js ?? "",
|
|
@@ -1883,6 +1960,63 @@ var TIPJAR_DEFAULT_MODULE = {
|
|
|
1883
1960
|
version: 1,
|
|
1884
1961
|
content: TIPJAR_DEFAULT_CONTENT_FIELDS
|
|
1885
1962
|
};
|
|
1963
|
+
var EVENTLIST_DEFAULT_CONTENT_FIELDS = {
|
|
1964
|
+
version: 2,
|
|
1965
|
+
eventListType: {},
|
|
1966
|
+
theme: "basic",
|
|
1967
|
+
themeConfig: {},
|
|
1968
|
+
borderRadius: "10px",
|
|
1969
|
+
condensedText: true,
|
|
1970
|
+
src: "",
|
|
1971
|
+
// Category blacklist — events whose Lumia type appears here are
|
|
1972
|
+
// hidden. SL `show_<type>: false` maps to inserting the equivalent
|
|
1973
|
+
// Lumia category here.
|
|
1974
|
+
filters: [],
|
|
1975
|
+
sites: [],
|
|
1976
|
+
maxItemsToShow: 20,
|
|
1977
|
+
removeAfter: 0,
|
|
1978
|
+
reverse: false,
|
|
1979
|
+
horizontal: false,
|
|
1980
|
+
animations: {
|
|
1981
|
+
enterAnimation: "fadeIn",
|
|
1982
|
+
exitAnimation: "fadeOut",
|
|
1983
|
+
enterAnimationDuration: 1e3,
|
|
1984
|
+
exitAnimationDuration: 1e3,
|
|
1985
|
+
enterAnimationDelay: 0,
|
|
1986
|
+
exitAnimationDelay: 0
|
|
1987
|
+
},
|
|
1988
|
+
showDetailedText: true,
|
|
1989
|
+
hideAlertMessage: false,
|
|
1990
|
+
showAlertIcon: true,
|
|
1991
|
+
showSiteIcon: false,
|
|
1992
|
+
showUserAvatar: false,
|
|
1993
|
+
fadeOutAfterDelay: false,
|
|
1994
|
+
fadeOutDelayTime: 5,
|
|
1995
|
+
image: "",
|
|
1996
|
+
itemGap: 16,
|
|
1997
|
+
audio: {
|
|
1998
|
+
enter: "",
|
|
1999
|
+
volume: 1
|
|
2000
|
+
}
|
|
2001
|
+
};
|
|
2002
|
+
var EVENTLIST_DEFAULT_MODULE = {
|
|
2003
|
+
version: 2,
|
|
2004
|
+
content: EVENTLIST_DEFAULT_CONTENT_FIELDS,
|
|
2005
|
+
css: {
|
|
2006
|
+
borderRadius: "0px",
|
|
2007
|
+
borderStyle: "solid",
|
|
2008
|
+
borderWidth: "0px",
|
|
2009
|
+
borderColor: "transparent",
|
|
2010
|
+
fontSize: 16,
|
|
2011
|
+
lineHeight: 1.2,
|
|
2012
|
+
textAlign: "left",
|
|
2013
|
+
fontFamily: "Roboto",
|
|
2014
|
+
fontWeight: "normal",
|
|
2015
|
+
color: "#ffffff",
|
|
2016
|
+
textShadow: "",
|
|
2017
|
+
background: "transparent"
|
|
2018
|
+
}
|
|
2019
|
+
};
|
|
1886
2020
|
|
|
1887
2021
|
// src/se-import/mappers/misc.ts
|
|
1888
2022
|
function mapChatboxTheme(seTheme) {
|
|
@@ -3832,11 +3966,6 @@ function rewriteAssetURLs(overlay, mapping) {
|
|
|
3832
3966
|
};
|
|
3833
3967
|
}
|
|
3834
3968
|
var MAX_MIRROR_ASSET_BYTES = 100 * 1024 * 1024;
|
|
3835
|
-
function looksLikeCorsError2(err) {
|
|
3836
|
-
if (!(err instanceof TypeError)) return false;
|
|
3837
|
-
const msg = (err.message ?? "").toLowerCase();
|
|
3838
|
-
return msg.includes("failed to fetch") || msg.includes("networkerror") || msg.includes("network error") || msg.includes("cors") || msg.includes("access-control") || err.message.length > 0;
|
|
3839
|
-
}
|
|
3840
3969
|
async function mirrorOneAsset(url, upload, signal, proxyAssetFetch) {
|
|
3841
3970
|
const blob = await fetchAssetBlob(url, signal, proxyAssetFetch);
|
|
3842
3971
|
if (signal?.aborted) throw new DOMException("Aborted", "AbortError");
|
|
@@ -3868,7 +3997,7 @@ async function fetchAssetBlob(url, signal, proxyAssetFetch) {
|
|
|
3868
3997
|
return res.blob();
|
|
3869
3998
|
}
|
|
3870
3999
|
if (signal?.aborted) throw new DOMException("Aborted", "AbortError");
|
|
3871
|
-
if (proxyAssetFetch &&
|
|
4000
|
+
if (proxyAssetFetch && looksLikeCorsError(directErr)) {
|
|
3872
4001
|
try {
|
|
3873
4002
|
return await proxyAssetFetch(url);
|
|
3874
4003
|
} catch (proxyErr) {
|
|
@@ -3919,7 +4048,7 @@ function guessMime(name) {
|
|
|
3919
4048
|
}
|
|
3920
4049
|
|
|
3921
4050
|
// src/se-import/transplant.ts
|
|
3922
|
-
import { nanoid as
|
|
4051
|
+
import { nanoid as nanoid4 } from "nanoid";
|
|
3923
4052
|
function cloneModuleForTransplant(srcModule, newId) {
|
|
3924
4053
|
const clonedContent = srcModule.content ? structuredClone(srcModule.content) : srcModule.content;
|
|
3925
4054
|
if (srcModule.settings?.type === "custom" && clonedContent) {
|
|
@@ -3937,7 +4066,7 @@ function transplantLayer(source, sourceLayerId) {
|
|
|
3937
4066
|
const srcLayer = layers.find((l) => l.id === sourceLayerId);
|
|
3938
4067
|
const srcModule = modules[sourceLayerId];
|
|
3939
4068
|
if (!srcLayer || !srcModule) return null;
|
|
3940
|
-
const rootNewId =
|
|
4069
|
+
const rootNewId = nanoid4();
|
|
3941
4070
|
if (srcLayer.type !== "group") {
|
|
3942
4071
|
const layer = { ...srcLayer, id: rootNewId };
|
|
3943
4072
|
const module = cloneModuleForTransplant(srcModule, rootNewId);
|
|
@@ -3955,7 +4084,7 @@ function transplantLayer(source, sourceLayerId) {
|
|
|
3955
4084
|
if (seen.has(child.id)) continue;
|
|
3956
4085
|
seen.add(child.id);
|
|
3957
4086
|
descendants.push(child);
|
|
3958
|
-
idRemap.set(child.id,
|
|
4087
|
+
idRemap.set(child.id, nanoid4());
|
|
3959
4088
|
if (child.type === "group") queue.push(child.id);
|
|
3960
4089
|
}
|
|
3961
4090
|
}
|
|
@@ -6441,7 +6570,74 @@ function readSLProvider(response) {
|
|
|
6441
6570
|
}
|
|
6442
6571
|
|
|
6443
6572
|
// src/sl-import/mappers/alert-box.ts
|
|
6444
|
-
import { nanoid as
|
|
6573
|
+
import { nanoid as nanoid6 } from "nanoid";
|
|
6574
|
+
import { LumiaAlertValues as LumiaAlertValues2 } from "@lumiastream/lumia-types";
|
|
6575
|
+
|
|
6576
|
+
// src/se-import/import-envelope.ts
|
|
6577
|
+
import { nanoid as nanoid5 } from "nanoid";
|
|
6578
|
+
function buildModuleEnvelope(opts) {
|
|
6579
|
+
const id = opts.id ?? nanoid5();
|
|
6580
|
+
const locked = opts.locked ?? false;
|
|
6581
|
+
const visible = opts.visible ?? true;
|
|
6582
|
+
const layer = {
|
|
6583
|
+
id,
|
|
6584
|
+
...opts.parentGroupId !== void 0 ? { group: opts.parentGroupId } : {},
|
|
6585
|
+
...opts.type_layer ? { type: opts.type_layer } : {},
|
|
6586
|
+
state: {
|
|
6587
|
+
visible,
|
|
6588
|
+
locked,
|
|
6589
|
+
...opts.expanded !== void 0 ? { expanded: opts.expanded } : {}
|
|
6590
|
+
},
|
|
6591
|
+
bounds: {
|
|
6592
|
+
x: opts.bounds.x,
|
|
6593
|
+
y: opts.bounds.y,
|
|
6594
|
+
width: opts.bounds.width,
|
|
6595
|
+
height: opts.bounds.height,
|
|
6596
|
+
scale: [1, 1],
|
|
6597
|
+
...opts.zIndex !== void 0 ? { zIndex: opts.zIndex } : {}
|
|
6598
|
+
}
|
|
6599
|
+
};
|
|
6600
|
+
const module = {
|
|
6601
|
+
id,
|
|
6602
|
+
version: opts.version ?? 1,
|
|
6603
|
+
loaded: true,
|
|
6604
|
+
settings: { type: opts.type, title: opts.title, locked },
|
|
6605
|
+
lights: opts.lights ?? [],
|
|
6606
|
+
css: opts.css ?? {},
|
|
6607
|
+
content: opts.content ?? {},
|
|
6608
|
+
variables: opts.variables ?? {},
|
|
6609
|
+
events: opts.events ?? {},
|
|
6610
|
+
...opts.alert !== void 0 ? { alert: opts.alert } : {}
|
|
6611
|
+
};
|
|
6612
|
+
return { id, layer, module };
|
|
6613
|
+
}
|
|
6614
|
+
var IMPORT_META_KEY = "importMeta";
|
|
6615
|
+
function stampImportMetaMerge(module, meta) {
|
|
6616
|
+
const existing = module.content?.[IMPORT_META_KEY] ?? {};
|
|
6617
|
+
module.content = {
|
|
6618
|
+
...module.content ?? {},
|
|
6619
|
+
[IMPORT_META_KEY]: {
|
|
6620
|
+
importedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6621
|
+
...existing,
|
|
6622
|
+
...meta
|
|
6623
|
+
}
|
|
6624
|
+
};
|
|
6625
|
+
}
|
|
6626
|
+
function customHtmlReviewItem(moduleId, importSourceType, settings, reason) {
|
|
6627
|
+
return {
|
|
6628
|
+
moduleId,
|
|
6629
|
+
// ReviewItem's `seWidget` is typed against SEWidget but the review
|
|
6630
|
+
// UI just reads `.type` and `.settings`. Cast through unknown is
|
|
6631
|
+
// the only place we touch the shape mismatch; widening the
|
|
6632
|
+
// ReviewItem type is a future cleanup.
|
|
6633
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
6634
|
+
seWidget: { type: importSourceType, settings },
|
|
6635
|
+
status: "template",
|
|
6636
|
+
reason
|
|
6637
|
+
};
|
|
6638
|
+
}
|
|
6639
|
+
|
|
6640
|
+
// src/sl-import/mappers/alert-box.ts
|
|
6445
6641
|
var SL_EVENT_ROUTING = {
|
|
6446
6642
|
// Platform-native — provider routes (Twitch / Kick / YouTube).
|
|
6447
6643
|
follow: { pickProviderKeys: "follower" },
|
|
@@ -6453,47 +6649,35 @@ var SL_EVENT_ROUTING = {
|
|
|
6453
6649
|
// `subscriber` here is YouTube subscribe (free); `sponsor` is YouTube
|
|
6454
6650
|
// channel membership; `fanfunding` is YouTube superchat. SL ships these
|
|
6455
6651
|
// even on Twitch-primary accounts so we always map them.
|
|
6456
|
-
subscriber: { keys: [
|
|
6457
|
-
sponsor: { keys: [
|
|
6458
|
-
fanfunding: { keys: [
|
|
6459
|
-
super_sticker: { keys: [
|
|
6652
|
+
subscriber: { keys: [LumiaAlertValues2.YOUTUBE_SUBSCRIBER] },
|
|
6653
|
+
sponsor: { keys: [LumiaAlertValues2.YOUTUBE_MEMBER] },
|
|
6654
|
+
fanfunding: { keys: [LumiaAlertValues2.YOUTUBE_SUPERCHAT] },
|
|
6655
|
+
super_sticker: { keys: [LumiaAlertValues2.YOUTUBE_SUPERSTICKER] },
|
|
6460
6656
|
// Donation / tip channels — Lumia ships dedicated alert keys for each
|
|
6461
6657
|
// integration. `donation` itself fans out to `streamlabs-donation` AND
|
|
6462
6658
|
// `lumiastream-donation` so the imported alert fires regardless of which
|
|
6463
6659
|
// channel the donation actually came in on.
|
|
6464
|
-
donation: { keys: [
|
|
6465
|
-
eldonation: { keys: [
|
|
6466
|
-
tiltifydonation: { keys: [
|
|
6467
|
-
donordrivedonation: { keys: [
|
|
6468
|
-
justgivingdonation: { keys: [
|
|
6469
|
-
pledge: { keys: [
|
|
6660
|
+
donation: { keys: [LumiaAlertValues2.STREAMLABS_DONATION, LumiaAlertValues2.LUMIASTREAM_DONATION] },
|
|
6661
|
+
eldonation: { keys: [LumiaAlertValues2.EXTRALIFE_DONATION] },
|
|
6662
|
+
tiltifydonation: { keys: [LumiaAlertValues2.TILTIFY_DONATION] },
|
|
6663
|
+
donordrivedonation: { keys: [LumiaAlertValues2.DONORDRIVE_DONATION] },
|
|
6664
|
+
justgivingdonation: { keys: [LumiaAlertValues2.STREAMLABS_DONATION] },
|
|
6665
|
+
pledge: { keys: [LumiaAlertValues2.PATREON_PLEDGE] },
|
|
6470
6666
|
// Charity / merch / loyalty / prime gifting — SL-flavored channels with
|
|
6471
6667
|
// matching Lumia keys.
|
|
6472
|
-
streamlabscharitydonation: { keys: [
|
|
6473
|
-
twitchcharitydonation: { keys: [
|
|
6474
|
-
merch: { keys: [
|
|
6475
|
-
loyalty_store_redemption: { keys: [
|
|
6476
|
-
prime_sub_gift: { keys: [
|
|
6477
|
-
treat: { keys: [
|
|
6668
|
+
streamlabscharitydonation: { keys: [LumiaAlertValues2.STREAMLABS_CHARITY] },
|
|
6669
|
+
twitchcharitydonation: { keys: [LumiaAlertValues2.TWITCH_CHARITY_DONATION] },
|
|
6670
|
+
merch: { keys: [LumiaAlertValues2.STREAMLABS_MERCH] },
|
|
6671
|
+
loyalty_store_redemption: { keys: [LumiaAlertValues2.STREAMLABS_REDEMPTION] },
|
|
6672
|
+
prime_sub_gift: { keys: [LumiaAlertValues2.STREAMLABS_PRIMEGIFT] },
|
|
6673
|
+
treat: { keys: [LumiaAlertValues2.TREATSTREAM_TREAT] },
|
|
6478
6674
|
// TikTok-flavored (jewel gift is TikTok-ish in concept).
|
|
6479
|
-
jewel_gift: { keys: [
|
|
6675
|
+
jewel_gift: { keys: [LumiaAlertValues2.TIKTOK_GIFT] }
|
|
6480
6676
|
};
|
|
6481
6677
|
function getField(settings, prefix, field) {
|
|
6482
6678
|
const key = `${prefix}_${field}`;
|
|
6483
6679
|
return settings[key];
|
|
6484
6680
|
}
|
|
6485
|
-
function pxToNumber(raw, fallback) {
|
|
6486
|
-
if (typeof raw === "number" && Number.isFinite(raw)) return raw;
|
|
6487
|
-
if (typeof raw !== "string") return fallback;
|
|
6488
|
-
const trimmed = raw.trim();
|
|
6489
|
-
if (!trimmed) return fallback;
|
|
6490
|
-
if (trimmed.endsWith("px")) {
|
|
6491
|
-
const n2 = parseFloat(trimmed.slice(0, -2));
|
|
6492
|
-
return Number.isFinite(n2) ? n2 : fallback;
|
|
6493
|
-
}
|
|
6494
|
-
const n = parseFloat(trimmed);
|
|
6495
|
-
return Number.isFinite(n) ? n : fallback;
|
|
6496
|
-
}
|
|
6497
6681
|
function translateLayout2(slLayout) {
|
|
6498
6682
|
switch (slLayout) {
|
|
6499
6683
|
case "above":
|
|
@@ -6603,9 +6787,7 @@ function buildBaseAlertEvent(settings, prefix, defaultMessage) {
|
|
|
6603
6787
|
tts: {
|
|
6604
6788
|
enabled: getField(settings, prefix, "tts_enabled") ?? false,
|
|
6605
6789
|
voice: getField(settings, prefix, "tts_language") ?? "Brian",
|
|
6606
|
-
volume: clampVolume(
|
|
6607
|
-
getField(settings, prefix, "tts_volume") ?? 75
|
|
6608
|
-
),
|
|
6790
|
+
volume: clampVolume(getField(settings, prefix, "tts_volume") ?? 75),
|
|
6609
6791
|
minAmount: getField(settings, prefix, "tts_min_amount") ?? 0
|
|
6610
6792
|
},
|
|
6611
6793
|
variations: []
|
|
@@ -6620,10 +6802,7 @@ function readSecondaryMessageConfig(settings, prefix, primaryFontFamily) {
|
|
|
6620
6802
|
const enabled = settings[showKey];
|
|
6621
6803
|
if (enabled !== true) return null;
|
|
6622
6804
|
const fontFamily = getField(settings, secondaryPrefix, "message_font") ?? primaryFontFamily;
|
|
6623
|
-
const fontSize = pxToNumber(
|
|
6624
|
-
getField(settings, secondaryPrefix, "message_font_size"),
|
|
6625
|
-
20
|
|
6626
|
-
);
|
|
6805
|
+
const fontSize = pxToNumber(getField(settings, secondaryPrefix, "message_font_size"), 20);
|
|
6627
6806
|
const fontWeight = getField(settings, secondaryPrefix, "message_font_weight") ?? 400;
|
|
6628
6807
|
const fontColor = getField(settings, secondaryPrefix, "message_font_color") ?? "#FFFFFF";
|
|
6629
6808
|
return {
|
|
@@ -6650,11 +6829,20 @@ function translateSLVariationCondition(v) {
|
|
|
6650
6829
|
const numericData = typeof data === "number" ? data : Number(data);
|
|
6651
6830
|
switch (v.condition) {
|
|
6652
6831
|
case "RANDOM":
|
|
6653
|
-
return {
|
|
6832
|
+
return {
|
|
6833
|
+
conditionType: "RANDOM",
|
|
6834
|
+
condition: Number.isFinite(numericData) ? numericData : 100
|
|
6835
|
+
};
|
|
6654
6836
|
case "MIN_BITS_USED":
|
|
6655
|
-
return {
|
|
6837
|
+
return {
|
|
6838
|
+
conditionType: "GREATER_NUMBER",
|
|
6839
|
+
condition: Number.isFinite(numericData) ? numericData : 0
|
|
6840
|
+
};
|
|
6656
6841
|
case "EXACT_BITS_USED":
|
|
6657
|
-
return {
|
|
6842
|
+
return {
|
|
6843
|
+
conditionType: "EQUAL_NUMBER",
|
|
6844
|
+
condition: Number.isFinite(numericData) ? numericData : 0
|
|
6845
|
+
};
|
|
6658
6846
|
case "MIN_MONTHS_SUBSCRIBED":
|
|
6659
6847
|
return {
|
|
6660
6848
|
conditionType: "SUBSCRIBED_MONTHS_GREATER",
|
|
@@ -6719,6 +6907,14 @@ function buildVariationEvent(v, parentBase) {
|
|
|
6719
6907
|
if (s.hideAnimation) inherited.animations.alertExitAnimation = s.hideAnimation;
|
|
6720
6908
|
if (s.layout) inherited.layout.value = translateLayout2(s.layout);
|
|
6721
6909
|
return {
|
|
6910
|
+
// Reuse SL's own `uuid` when present so re-importing the same SL
|
|
6911
|
+
// alert-box keeps the variation identity stable across import
|
|
6912
|
+
// runs. SL always emits a uuid in their config (every fixture we've
|
|
6913
|
+
// seen has one); fall back to nanoid for the edge case of a
|
|
6914
|
+
// hand-crafted / older payload that omits it. Matches the SE
|
|
6915
|
+
// alert mapper's variation-id pattern — see Settings.tsx auto-fill
|
|
6916
|
+
// note there for why a stable id matters from save #1.
|
|
6917
|
+
id: typeof v.uuid === "string" && v.uuid.length > 0 ? v.uuid : nanoid6(),
|
|
6722
6918
|
name: v.name ?? "Variation",
|
|
6723
6919
|
on: v.paused !== true,
|
|
6724
6920
|
randomChance: 100,
|
|
@@ -6784,11 +6980,7 @@ function buildSLAlertBox(settings, options = {}) {
|
|
|
6784
6980
|
for (const [prefix, routing] of Object.entries(SL_EVENT_ROUTING)) {
|
|
6785
6981
|
const lumiaKeys = routing.pickProviderKeys ? alertBoxKeysFor(routing.pickProviderKeys, provider) : routing.keys ?? [];
|
|
6786
6982
|
if (lumiaKeys.length === 0) continue;
|
|
6787
|
-
const built = buildBaseAlertEvent(
|
|
6788
|
-
settings,
|
|
6789
|
-
prefix,
|
|
6790
|
-
DEFAULT_MESSAGES[prefix] ?? "{name}"
|
|
6791
|
-
);
|
|
6983
|
+
const built = buildBaseAlertEvent(settings, prefix, DEFAULT_MESSAGES[prefix] ?? "{name}");
|
|
6792
6984
|
if (!built) continue;
|
|
6793
6985
|
const variationKey = VARIATION_KEY_FOR_PREFIX[prefix];
|
|
6794
6986
|
const variations = variationKey ? settings[variationKey] : void 0;
|
|
@@ -6805,53 +6997,32 @@ function buildSLAlertBox(settings, options = {}) {
|
|
|
6805
6997
|
channelsImported.push(prefix);
|
|
6806
6998
|
if (firstEvent == null) firstEvent = built;
|
|
6807
6999
|
}
|
|
6808
|
-
const id = nanoid4();
|
|
6809
|
-
const layer = {
|
|
6810
|
-
id,
|
|
6811
|
-
state: { visible: true, locked: false },
|
|
6812
|
-
bounds: { x: 100, y: 100, width: 700, height: 600, scale: [1, 1] }
|
|
6813
|
-
};
|
|
6814
7000
|
const baseAlert = firstEvent ? stripEventOnlyFields(firstEvent) : {};
|
|
6815
|
-
const module = {
|
|
6816
|
-
|
|
6817
|
-
|
|
6818
|
-
|
|
6819
|
-
settings: { type: "alert", title: "Streamlabs Alerts", locked: false },
|
|
6820
|
-
lights: [],
|
|
6821
|
-
css: {},
|
|
6822
|
-
content: {},
|
|
6823
|
-
variables: {},
|
|
6824
|
-
events: {},
|
|
7001
|
+
const { layer, module } = buildModuleEnvelope({
|
|
7002
|
+
type: "alert",
|
|
7003
|
+
title: "Streamlabs Alerts",
|
|
7004
|
+
bounds: { x: 100, y: 100, width: 700, height: 600 },
|
|
6825
7005
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
6826
7006
|
alert: { ...baseAlert, events }
|
|
6827
|
-
};
|
|
7007
|
+
});
|
|
6828
7008
|
return { layer, module, channelsImported };
|
|
6829
7009
|
}
|
|
6830
7010
|
function stripEventOnlyFields(event) {
|
|
6831
|
-
const {
|
|
7011
|
+
const {
|
|
7012
|
+
on: _on,
|
|
7013
|
+
variations: _variations,
|
|
7014
|
+
matchEmptyCondition: _mec,
|
|
7015
|
+
...base
|
|
7016
|
+
} = event;
|
|
6832
7017
|
return base;
|
|
6833
7018
|
}
|
|
6834
7019
|
|
|
6835
7020
|
// src/sl-import/mappers/tip-jar.ts
|
|
6836
|
-
import { nanoid as nanoid5 } from "nanoid";
|
|
6837
|
-
function pxToNumber2(raw, fallback) {
|
|
6838
|
-
if (typeof raw === "number" && Number.isFinite(raw)) return raw;
|
|
6839
|
-
if (typeof raw !== "string") return fallback;
|
|
6840
|
-
const trimmed = raw.trim();
|
|
6841
|
-
if (!trimmed) return fallback;
|
|
6842
|
-
if (trimmed.endsWith("px")) {
|
|
6843
|
-
const n2 = parseFloat(trimmed.slice(0, -2));
|
|
6844
|
-
return Number.isFinite(n2) ? n2 : fallback;
|
|
6845
|
-
}
|
|
6846
|
-
const n = parseFloat(trimmed);
|
|
6847
|
-
return Number.isFinite(n) ? n : fallback;
|
|
6848
|
-
}
|
|
6849
7021
|
function buildSLTipJar(settings) {
|
|
6850
|
-
const id = nanoid5();
|
|
6851
7022
|
const backgroundColor = settings.background?.color ?? "transparent";
|
|
6852
7023
|
const textColor = settings.text?.color ?? "#FFFFFF";
|
|
6853
7024
|
const fontFamily = settings.text?.font ?? "Open Sans";
|
|
6854
|
-
const fontSize =
|
|
7025
|
+
const fontSize = pxToNumber(settings.text?.size, 32);
|
|
6855
7026
|
const types = settings.types ?? {};
|
|
6856
7027
|
const tipsConfig = types.tips ?? {};
|
|
6857
7028
|
const slEvents = {
|
|
@@ -6867,17 +7038,11 @@ function buildSLTipJar(settings) {
|
|
|
6867
7038
|
...TIPJAR_DEFAULT_MODULE.content.display,
|
|
6868
7039
|
showEventMessages: settings.text?.show !== false
|
|
6869
7040
|
};
|
|
6870
|
-
const layer = {
|
|
6871
|
-
|
|
6872
|
-
|
|
6873
|
-
bounds: { x: 100, y: 100, width: 600, height: 700
|
|
6874
|
-
};
|
|
6875
|
-
const module = {
|
|
6876
|
-
id,
|
|
7041
|
+
const { layer, module } = buildModuleEnvelope({
|
|
7042
|
+
type: "tipjar",
|
|
7043
|
+
title: "Streamlabs Tip Jar",
|
|
7044
|
+
bounds: { x: 100, y: 100, width: 600, height: 700 },
|
|
6877
7045
|
version: TIPJAR_DEFAULT_MODULE.version,
|
|
6878
|
-
loaded: true,
|
|
6879
|
-
settings: { type: "tipjar", title: "Streamlabs Tip Jar", locked: false },
|
|
6880
|
-
lights: [],
|
|
6881
7046
|
css: {
|
|
6882
7047
|
fontFamily,
|
|
6883
7048
|
fontSize,
|
|
@@ -6898,12 +7063,7 @@ function buildSLTipJar(settings) {
|
|
|
6898
7063
|
source: "streamlabs",
|
|
6899
7064
|
widgetType: "tip_jar",
|
|
6900
7065
|
importedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6901
|
-
// SL jar type doesn't have a 1:1 mapping to Lumia's preset
|
|
6902
|
-
// vocabulary (cup-01, cup-02, …). Stashed for review.
|
|
6903
7066
|
slJarType: settings.jar?.type ?? null,
|
|
6904
|
-
// SL tier coin tiers — Lumia's sprite tiers are per-event and
|
|
6905
|
-
// configured in the sprite editor; the user re-creates these
|
|
6906
|
-
// manually. Stored here so they're not lost.
|
|
6907
7067
|
slTipTiers: Array.isArray(tipsConfig.tiers) ? tipsConfig.tiers.map((t) => ({
|
|
6908
7068
|
minimumAmount: t.minimum_amount,
|
|
6909
7069
|
imageSrc: t.image_src ?? null
|
|
@@ -6911,15 +7071,12 @@ function buildSLTipJar(settings) {
|
|
|
6911
7071
|
slMinimumTipAmount: tipsConfig.minimum_amount ?? null,
|
|
6912
7072
|
slHadCustomHtml: settings.custom_html_enabled === true
|
|
6913
7073
|
}
|
|
6914
|
-
}
|
|
6915
|
-
|
|
6916
|
-
events: {}
|
|
6917
|
-
};
|
|
7074
|
+
}
|
|
7075
|
+
});
|
|
6918
7076
|
return { layer, module };
|
|
6919
7077
|
}
|
|
6920
7078
|
|
|
6921
7079
|
// src/sl-import/mappers/goal.ts
|
|
6922
|
-
import { nanoid as nanoid6 } from "nanoid";
|
|
6923
7080
|
var GOAL_SOURCE_MAP = {
|
|
6924
7081
|
follower_goal: "follower",
|
|
6925
7082
|
sub_goal: "subscriber",
|
|
@@ -6932,69 +7089,262 @@ var GOAL_TITLE_MAP = {
|
|
|
6932
7089
|
donation_goal: "Donation Goal",
|
|
6933
7090
|
bit_goal: "Bits Goal"
|
|
6934
7091
|
};
|
|
6935
|
-
|
|
6936
|
-
|
|
6937
|
-
|
|
6938
|
-
|
|
6939
|
-
|
|
6940
|
-
|
|
6941
|
-
const n2 = parseFloat(trimmed.slice(0, -2));
|
|
6942
|
-
return Number.isFinite(n2) ? n2 : fallback;
|
|
6943
|
-
}
|
|
6944
|
-
const n = parseFloat(trimmed);
|
|
6945
|
-
return Number.isFinite(n) ? n : fallback;
|
|
6946
|
-
}
|
|
7092
|
+
var GOAL_CURRENT_TEMPLATE = {
|
|
7093
|
+
follower_goal: "{{twitch_total_follower_count}}",
|
|
7094
|
+
sub_goal: "{{twitch_total_subscriber_count}}",
|
|
7095
|
+
donation_goal: "{{total_donation_amount}}",
|
|
7096
|
+
bit_goal: "{{twitch_total_bits_count}}"
|
|
7097
|
+
};
|
|
6947
7098
|
function buildSLGoal(settings, widgetType) {
|
|
6948
|
-
const id = nanoid6();
|
|
6949
7099
|
const goalSource = GOAL_SOURCE_MAP[widgetType] ?? "follower";
|
|
6950
7100
|
const defaultTitle = GOAL_TITLE_MAP[widgetType] ?? "Goal";
|
|
7101
|
+
const currentTemplate = GOAL_CURRENT_TEMPLATE[widgetType] ?? "";
|
|
7102
|
+
const requiresReview = settings.custom_enabled === true;
|
|
6951
7103
|
const title = typeof settings.title === "string" ? settings.title : defaultTitle;
|
|
6952
7104
|
const goalAmount = typeof settings.goal === "number" ? settings.goal : 100;
|
|
6953
7105
|
const currentAmount = typeof settings.current === "number" ? settings.current : 0;
|
|
6954
7106
|
const startingAmount = typeof settings.starting_amount === "number" ? settings.starting_amount : 0;
|
|
6955
|
-
const endsAt = typeof settings.ends_at === "string" ? settings.ends_at :
|
|
7107
|
+
const endsAt = typeof settings.ends_at === "string" ? settings.ends_at : "";
|
|
6956
7108
|
const barColor = settings.bar_color ?? "#32C3A6";
|
|
6957
|
-
const
|
|
7109
|
+
const barBgColor = settings.bar_bg_color ?? "#181818";
|
|
7110
|
+
const widgetBgColor = settings.background_color ?? "transparent";
|
|
6958
7111
|
const textColor = settings.text_color ?? "#FFFFFF";
|
|
6959
7112
|
const barTextColor = settings.bar_text_color ?? textColor;
|
|
6960
7113
|
const fontFamily = settings.font ?? "Open Sans";
|
|
6961
|
-
const
|
|
6962
|
-
|
|
6963
|
-
|
|
6964
|
-
|
|
6965
|
-
|
|
6966
|
-
|
|
6967
|
-
|
|
6968
|
-
version: 1,
|
|
6969
|
-
loaded: true,
|
|
6970
|
-
settings: { type: "goal", title, locked: false },
|
|
6971
|
-
lights: [],
|
|
7114
|
+
const fontSize = pxToNumber(settings.font_size, 24);
|
|
7115
|
+
const barThickness = pxToNumber(settings.bar_thickness, 48);
|
|
7116
|
+
const layerHeight = Math.max(60, barThickness + 32);
|
|
7117
|
+
const { layer, module } = buildModuleEnvelope({
|
|
7118
|
+
type: "goal",
|
|
7119
|
+
title,
|
|
7120
|
+
bounds: { x: 100, y: 100, width: 740, height: layerHeight },
|
|
6972
7121
|
css: {
|
|
6973
7122
|
fontFamily,
|
|
6974
7123
|
color: textColor,
|
|
6975
|
-
background:
|
|
7124
|
+
background: widgetBgColor,
|
|
7125
|
+
fontSize
|
|
6976
7126
|
},
|
|
7127
|
+
// Use Lumia's canonical goal field names — `targetGoal` (string),
|
|
7128
|
+
// `current` (template string), `display`, `filledColor` /
|
|
7129
|
+
// `unfilledColor`. The prior schema used invented field names
|
|
7130
|
+
// (`goalAmount`, `barColor`) that the renderer ignored.
|
|
6977
7131
|
content: {
|
|
6978
|
-
|
|
6979
|
-
|
|
6980
|
-
|
|
6981
|
-
|
|
6982
|
-
|
|
6983
|
-
|
|
6984
|
-
|
|
6985
|
-
|
|
6986
|
-
|
|
6987
|
-
|
|
6988
|
-
|
|
7132
|
+
version: 1,
|
|
7133
|
+
type: "bar",
|
|
7134
|
+
label: title,
|
|
7135
|
+
subLabel: "",
|
|
7136
|
+
display: "{{current}} / {{goal}}",
|
|
7137
|
+
current: currentAmount > 0 ? String(currentAmount) : currentTemplate,
|
|
7138
|
+
targetGoal: String(goalAmount),
|
|
7139
|
+
image: "",
|
|
7140
|
+
audio: {
|
|
7141
|
+
decrement: "",
|
|
7142
|
+
increment: "",
|
|
7143
|
+
goal: "",
|
|
7144
|
+
goalFailed: "",
|
|
7145
|
+
volume: 1
|
|
7146
|
+
},
|
|
7147
|
+
endDate: endsAt,
|
|
7148
|
+
goalAnimation: "",
|
|
7149
|
+
goalAnimationLoop: true,
|
|
7150
|
+
unfilledColor: barBgColor,
|
|
7151
|
+
filledColor: barColor,
|
|
7152
|
+
alignItemsToEdges: false,
|
|
7153
|
+
border: "solid 1px transparent",
|
|
7154
|
+
borderRadius: "40px",
|
|
7155
|
+
highlightColor: "inherit",
|
|
7156
|
+
importMeta: {
|
|
7157
|
+
source: "streamlabs",
|
|
7158
|
+
widgetType,
|
|
7159
|
+
importedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7160
|
+
goalSource,
|
|
7161
|
+
slLayout: settings.layout ?? null,
|
|
7162
|
+
slBarTextColor: barTextColor,
|
|
7163
|
+
slBarThickness: barThickness,
|
|
7164
|
+
slStartingAmount: startingAmount,
|
|
7165
|
+
slHadCustomHtml: settings.custom_enabled === true
|
|
7166
|
+
}
|
|
7167
|
+
}
|
|
7168
|
+
});
|
|
7169
|
+
return { layer, module, requiresReview };
|
|
7170
|
+
}
|
|
7171
|
+
|
|
7172
|
+
// src/sl-import/mappers/chat-box.ts
|
|
7173
|
+
function buildSitesArray(settings) {
|
|
7174
|
+
const defaultSites = [...CHATBOX_DEFAULT_MODULE.content.sites];
|
|
7175
|
+
const drop = /* @__PURE__ */ new Set();
|
|
7176
|
+
if (settings.show_bttv_emotes === false) drop.add("bttv");
|
|
7177
|
+
if (settings.show_franker_emotes === false) drop.add("ffz");
|
|
7178
|
+
if (settings.show_7tv_emotes === false) drop.add("seventv");
|
|
7179
|
+
if (settings.twitch_enabled === false) drop.add("twitch");
|
|
7180
|
+
if (settings.youtube_enabled === false) drop.add("youtube");
|
|
7181
|
+
if (settings.facebook_enabled === false) drop.add("facebook");
|
|
7182
|
+
if (settings.tiktok_enabled === false) drop.add("tiktok");
|
|
7183
|
+
if (settings.kick_enabled === false) drop.add("kick");
|
|
7184
|
+
if (drop.size === 0) return defaultSites;
|
|
7185
|
+
return defaultSites.filter((s) => !drop.has(s));
|
|
7186
|
+
}
|
|
7187
|
+
function buildSLChatBox(settings) {
|
|
7188
|
+
const requiresReview = settings.custom_enabled === true;
|
|
7189
|
+
const backgroundColor = settings.background_color ?? "transparent";
|
|
7190
|
+
const textColor = settings.text_color ?? "#FFFFFF";
|
|
7191
|
+
const textSize = pxToNumber(settings.text_size, 16);
|
|
7192
|
+
const fadeOutAfterDelay = settings.always_show_messages !== true;
|
|
7193
|
+
const fadeOutDelayTime = fadeOutAfterDelay ? Math.max(1, Math.round((settings.message_hide_delay ?? 15e3) / 1e3)) : 0;
|
|
7194
|
+
const sites = buildSitesArray(settings);
|
|
7195
|
+
const { layer, module } = buildModuleEnvelope({
|
|
7196
|
+
type: "chatbox",
|
|
7197
|
+
title: "Streamlabs Chat Box",
|
|
7198
|
+
bounds: { x: 100, y: 100, width: 375, height: 470 },
|
|
7199
|
+
version: CHATBOX_DEFAULT_MODULE.version,
|
|
7200
|
+
css: {
|
|
7201
|
+
background: backgroundColor,
|
|
7202
|
+
color: textColor,
|
|
7203
|
+
fontSize: textSize
|
|
6989
7204
|
},
|
|
6990
|
-
|
|
6991
|
-
|
|
6992
|
-
|
|
6993
|
-
|
|
7205
|
+
content: {
|
|
7206
|
+
// Spread canonical defaults FIRST so the renderer's dereferences
|
|
7207
|
+
// (chatboxStreamingSite.<platform>.themeConfig, animations,
|
|
7208
|
+
// chatboxStreamingSite.<platform>.message, etc.) all exist.
|
|
7209
|
+
...CHATBOX_DEFAULT_MODULE.content,
|
|
7210
|
+
// Then override the bits SL controls.
|
|
7211
|
+
sites,
|
|
7212
|
+
fadeOutAfterDelay,
|
|
7213
|
+
fadeOutDelayTime,
|
|
7214
|
+
// SL keeps history forever by default — `always_show_messages`
|
|
7215
|
+
// implies the streamer wants a permanent scrollback. Lumia
|
|
7216
|
+
// exposes that as `permanent: true`.
|
|
7217
|
+
permanent: settings.always_show_messages === true,
|
|
7218
|
+
// Preserve the SL-specific provenance for the review step.
|
|
7219
|
+
importMeta: {
|
|
7220
|
+
source: "streamlabs",
|
|
7221
|
+
widgetType: "chat_box",
|
|
7222
|
+
importedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7223
|
+
slTheme: settings.theme ?? null,
|
|
7224
|
+
slHadCustomHtml: settings.custom_enabled === true,
|
|
7225
|
+
slHidesCommonBots: settings.hide_common_chat_bots === true,
|
|
7226
|
+
slHidesCommands: settings.hide_commands === true,
|
|
7227
|
+
slMutedChatters: settings.muted_chatters ?? "",
|
|
7228
|
+
slMessageShowDelayMs: settings.message_show_delay ?? 0
|
|
7229
|
+
}
|
|
7230
|
+
}
|
|
7231
|
+
});
|
|
7232
|
+
return { layer, module, requiresReview };
|
|
7233
|
+
}
|
|
7234
|
+
|
|
7235
|
+
// src/sl-import/mappers/event-list.ts
|
|
7236
|
+
var SL_SHOW_FLAG_TO_LUMIA_FILTER = {
|
|
7237
|
+
show_follows: "follower",
|
|
7238
|
+
show_subscriptions: "subscribers",
|
|
7239
|
+
show_resubs: "subscribers",
|
|
7240
|
+
show_sub_gifts: "gifts",
|
|
7241
|
+
show_subscribers: "subscribers",
|
|
7242
|
+
show_sponsors: "subscribers",
|
|
7243
|
+
// YouTube channel members
|
|
7244
|
+
show_sponsor_gifts: "gifts",
|
|
7245
|
+
show_bits: "bits",
|
|
7246
|
+
show_donations: "donation",
|
|
7247
|
+
show_pledges: "donation",
|
|
7248
|
+
// Patreon
|
|
7249
|
+
show_eldonations: "donation",
|
|
7250
|
+
show_tiltifydonations: "donation",
|
|
7251
|
+
show_donordrivedonations: "donation",
|
|
7252
|
+
show_justgivingdonations: "donation",
|
|
7253
|
+
show_streamlabscharitydonations: "donation",
|
|
7254
|
+
show_twitchcharitydonations: "donation",
|
|
7255
|
+
show_twitch_charity: "donation",
|
|
7256
|
+
show_treats: "donation",
|
|
7257
|
+
// TreatStream
|
|
7258
|
+
show_merch: "purchases",
|
|
7259
|
+
show_raids: "raids",
|
|
7260
|
+
show_hosts: "raids",
|
|
7261
|
+
show_redemptions: "redemption",
|
|
7262
|
+
show_fanfundings: "superchats",
|
|
7263
|
+
// YouTube Super Chat
|
|
7264
|
+
show_super_stickers: "superstickers",
|
|
7265
|
+
show_jewel_gifts: "stars"
|
|
7266
|
+
// TikTok-flavored, closest analogue
|
|
7267
|
+
};
|
|
7268
|
+
function buildFilters(settings) {
|
|
7269
|
+
const drop = /* @__PURE__ */ new Set();
|
|
7270
|
+
for (const [slFlag, lumiaCategory] of Object.entries(SL_SHOW_FLAG_TO_LUMIA_FILTER)) {
|
|
7271
|
+
if (settings[slFlag] === false) {
|
|
7272
|
+
drop.add(lumiaCategory);
|
|
7273
|
+
}
|
|
7274
|
+
}
|
|
7275
|
+
return Array.from(drop);
|
|
7276
|
+
}
|
|
7277
|
+
function buildSLEventList(settings) {
|
|
7278
|
+
const requiresReview = settings.custom_enabled === true;
|
|
7279
|
+
const backgroundColor = settings.background_color ?? "transparent";
|
|
7280
|
+
const textColor = settings.text_color ?? "#FFFFFF";
|
|
7281
|
+
const textSize = pxToNumber(settings.text_size, 16);
|
|
7282
|
+
const fontFamily = settings.font_family ?? "Roboto";
|
|
7283
|
+
const maxEvents = typeof settings.max_events === "number" && settings.max_events > 0 ? settings.max_events : EVENTLIST_DEFAULT_MODULE.content.maxItemsToShow;
|
|
7284
|
+
const enterAnimation = settings.show_animation ?? "fadeIn";
|
|
7285
|
+
const exitAnimation = settings.hide_animation ?? "fadeOut";
|
|
7286
|
+
const animationDuration = typeof settings.animation_speed === "number" ? settings.animation_speed : 1e3;
|
|
7287
|
+
const filters = buildFilters(settings);
|
|
7288
|
+
const { layer, module } = buildModuleEnvelope({
|
|
7289
|
+
type: "eventlist",
|
|
7290
|
+
title: "Streamlabs Event List",
|
|
7291
|
+
bounds: { x: 100, y: 100, width: 375, height: 470 },
|
|
7292
|
+
version: EVENTLIST_DEFAULT_MODULE.version,
|
|
7293
|
+
css: {
|
|
7294
|
+
...EVENTLIST_DEFAULT_MODULE.css,
|
|
7295
|
+
background: backgroundColor,
|
|
7296
|
+
color: textColor,
|
|
7297
|
+
fontSize: textSize,
|
|
7298
|
+
fontFamily
|
|
7299
|
+
},
|
|
7300
|
+
content: {
|
|
7301
|
+
// Spread canonical defaults FIRST so the renderer's dereferences
|
|
7302
|
+
// don't crash on missing nested objects.
|
|
7303
|
+
...EVENTLIST_DEFAULT_MODULE.content,
|
|
7304
|
+
maxItemsToShow: maxEvents,
|
|
7305
|
+
// SL's `flip_y` corresponds to Lumia's `reverse` (newest item
|
|
7306
|
+
// at the bottom vs the top). SL's `flip_x` flips the alignment
|
|
7307
|
+
// to right; Lumia covers that via css.textAlign but doesn't
|
|
7308
|
+
// have a per-module horizontal flip — stash as importMeta for
|
|
7309
|
+
// the review step.
|
|
7310
|
+
reverse: settings.flip_y === true,
|
|
7311
|
+
// SL's `keep_history: false` means events disappear after the
|
|
7312
|
+
// fade time; map to `fadeOutAfterDelay: true` with the fade
|
|
7313
|
+
// time SL configured.
|
|
7314
|
+
fadeOutAfterDelay: settings.keep_history === false,
|
|
7315
|
+
fadeOutDelayTime: typeof settings.fade_time === "number" && settings.fade_time > 0 ? Math.max(1, Math.round(settings.fade_time / 1e3)) : EVENTLIST_DEFAULT_MODULE.content.fadeOutDelayTime,
|
|
7316
|
+
animations: {
|
|
7317
|
+
...EVENTLIST_DEFAULT_MODULE.content.animations,
|
|
7318
|
+
enterAnimation,
|
|
7319
|
+
exitAnimation,
|
|
7320
|
+
enterAnimationDuration: animationDuration,
|
|
7321
|
+
exitAnimationDuration: animationDuration
|
|
7322
|
+
},
|
|
7323
|
+
filters,
|
|
7324
|
+
importMeta: {
|
|
7325
|
+
source: "streamlabs",
|
|
7326
|
+
widgetType: "event_list",
|
|
7327
|
+
importedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7328
|
+
slTheme: settings.theme ?? null,
|
|
7329
|
+
slThemeColor: settings.theme_color ?? null,
|
|
7330
|
+
slFlipX: settings.flip_x === true,
|
|
7331
|
+
slHadCustomHtml: settings.custom_enabled === true,
|
|
7332
|
+
// Per-event-type minimums SL filters by — Lumia's eventlist
|
|
7333
|
+
// has no equivalent thresholding; preserve so the streamer
|
|
7334
|
+
// can recreate via the alert module's variation conditions.
|
|
7335
|
+
slHostViewerMinimum: settings.host_viewer_minimum ?? 0,
|
|
7336
|
+
slBitsMinimum: settings.bits_minimum ?? 0,
|
|
7337
|
+
slRaidRaiderMinimum: settings.raid_raider_minimum ?? 0
|
|
7338
|
+
}
|
|
7339
|
+
}
|
|
7340
|
+
});
|
|
7341
|
+
return { layer, module, requiresReview };
|
|
6994
7342
|
}
|
|
6995
7343
|
|
|
6996
7344
|
// src/sl-import/sl-dispatch.ts
|
|
6997
|
-
|
|
7345
|
+
function buildSLDescription(widgetType, sourceUrl) {
|
|
7346
|
+
return buildImportDescription(`Imported from Streamlabs ${widgetType}`, sourceUrl);
|
|
7347
|
+
}
|
|
6998
7348
|
var GOAL_WIDGET_TYPES = /* @__PURE__ */ new Set([
|
|
6999
7349
|
"follower_goal",
|
|
7000
7350
|
"sub_goal",
|
|
@@ -7002,8 +7352,6 @@ var GOAL_WIDGET_TYPES = /* @__PURE__ */ new Set([
|
|
|
7002
7352
|
"bit_goal"
|
|
7003
7353
|
]);
|
|
7004
7354
|
var PLACEHOLDER_WIDGET_TYPES = /* @__PURE__ */ new Set([
|
|
7005
|
-
"chat_box",
|
|
7006
|
-
"event_list",
|
|
7007
7355
|
"donation_ticker",
|
|
7008
7356
|
"stream_boss",
|
|
7009
7357
|
"credits",
|
|
@@ -7013,8 +7361,6 @@ var PLACEHOLDER_WIDGET_TYPES = /* @__PURE__ */ new Set([
|
|
|
7013
7361
|
"streamlabels"
|
|
7014
7362
|
]);
|
|
7015
7363
|
var PLACEHOLDER_REASONS = {
|
|
7016
|
-
chat_box: "Lumia has a native chatbox module \u2014 set it up directly rather than importing the SL config.",
|
|
7017
|
-
event_list: "Lumia has a native eventlist module \u2014 set it up directly rather than importing the SL config.",
|
|
7018
7364
|
donation_ticker: "No native Lumia equivalent yet \u2014 imported as a placeholder text layer.",
|
|
7019
7365
|
stream_boss: "Lumia has a native streamboss module \u2014 set it up directly rather than importing the SL config.",
|
|
7020
7366
|
credits: "Lumia has a native credits module \u2014 set it up directly rather than importing the SL config.",
|
|
@@ -7023,19 +7369,14 @@ var PLACEHOLDER_REASONS = {
|
|
|
7023
7369
|
sub_train: "No native Lumia equivalent yet \u2014 imported as a placeholder text layer.",
|
|
7024
7370
|
streamlabels: "Use Lumia template variables (e.g. {{twitch_last_follower}}) inside a Text module instead of importing the SL streamlabels config."
|
|
7025
7371
|
};
|
|
7372
|
+
var CUSTOM_HTML_REVIEW_REASON = "The Streamlabs widget used custom HTML/CSS/JS that Lumia cannot host. The native Lumia module is the right starting point \u2014 recreate the styling there. The original SL settings are stashed under module.content.importMeta for reference.";
|
|
7026
7373
|
function stampImportMeta(module, widgetType, role, extra = {}) {
|
|
7027
|
-
|
|
7028
|
-
|
|
7029
|
-
|
|
7030
|
-
|
|
7031
|
-
|
|
7032
|
-
|
|
7033
|
-
role,
|
|
7034
|
-
importedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7035
|
-
...existing,
|
|
7036
|
-
...extra
|
|
7037
|
-
}
|
|
7038
|
-
};
|
|
7374
|
+
stampImportMetaMerge(module, {
|
|
7375
|
+
source: "streamlabs",
|
|
7376
|
+
widgetType,
|
|
7377
|
+
role,
|
|
7378
|
+
...extra
|
|
7379
|
+
});
|
|
7039
7380
|
}
|
|
7040
7381
|
function buildPlaceholder(widgetType) {
|
|
7041
7382
|
const id = nanoid7();
|
|
@@ -7067,7 +7408,7 @@ function buildPlaceholder(widgetType) {
|
|
|
7067
7408
|
};
|
|
7068
7409
|
return { layer, module };
|
|
7069
7410
|
}
|
|
7070
|
-
function importStreamlabsWidget(response, widgetType) {
|
|
7411
|
+
function importStreamlabsWidget(response, widgetType, options = {}) {
|
|
7071
7412
|
if (!isSLWidgetConfigResponse(response)) {
|
|
7072
7413
|
throw new Error(
|
|
7073
7414
|
"Streamlabs response is missing the expected `settings` envelope. The widget may be private or the response format may have changed."
|
|
@@ -7117,9 +7458,61 @@ function importStreamlabsWidget(response, widgetType) {
|
|
|
7117
7458
|
coverage.mappings.push({
|
|
7118
7459
|
seType: `streamlabs:${widgetType}`,
|
|
7119
7460
|
lumiaType: "goal",
|
|
7120
|
-
status: "direct",
|
|
7461
|
+
status: built.requiresReview ? "partial" : "direct",
|
|
7121
7462
|
count: 1
|
|
7122
7463
|
});
|
|
7464
|
+
if (built.requiresReview) {
|
|
7465
|
+
reviewItems.push(
|
|
7466
|
+
customHtmlReviewItem(
|
|
7467
|
+
built.module.id,
|
|
7468
|
+
`streamlabs-${widgetType}`,
|
|
7469
|
+
response.settings,
|
|
7470
|
+
CUSTOM_HTML_REVIEW_REASON
|
|
7471
|
+
)
|
|
7472
|
+
);
|
|
7473
|
+
}
|
|
7474
|
+
} else if (widgetType === "chat_box") {
|
|
7475
|
+
const built = buildSLChatBox(response.settings);
|
|
7476
|
+
stampImportMeta(built.module, widgetType, "chatbox");
|
|
7477
|
+
layers.push(built.layer);
|
|
7478
|
+
modules[built.layer.id] = built.module;
|
|
7479
|
+
coverage.mappings.push({
|
|
7480
|
+
seType: "streamlabs:chat_box",
|
|
7481
|
+
lumiaType: "chatbox",
|
|
7482
|
+
status: built.requiresReview ? "partial" : "direct",
|
|
7483
|
+
count: 1
|
|
7484
|
+
});
|
|
7485
|
+
if (built.requiresReview) {
|
|
7486
|
+
reviewItems.push(
|
|
7487
|
+
customHtmlReviewItem(
|
|
7488
|
+
built.module.id,
|
|
7489
|
+
"streamlabs-chat_box",
|
|
7490
|
+
response.settings,
|
|
7491
|
+
CUSTOM_HTML_REVIEW_REASON
|
|
7492
|
+
)
|
|
7493
|
+
);
|
|
7494
|
+
}
|
|
7495
|
+
} else if (widgetType === "event_list") {
|
|
7496
|
+
const built = buildSLEventList(response.settings);
|
|
7497
|
+
stampImportMeta(built.module, widgetType, "eventlist");
|
|
7498
|
+
layers.push(built.layer);
|
|
7499
|
+
modules[built.layer.id] = built.module;
|
|
7500
|
+
coverage.mappings.push({
|
|
7501
|
+
seType: "streamlabs:event_list",
|
|
7502
|
+
lumiaType: "eventlist",
|
|
7503
|
+
status: built.requiresReview ? "partial" : "direct",
|
|
7504
|
+
count: 1
|
|
7505
|
+
});
|
|
7506
|
+
if (built.requiresReview) {
|
|
7507
|
+
reviewItems.push(
|
|
7508
|
+
customHtmlReviewItem(
|
|
7509
|
+
built.module.id,
|
|
7510
|
+
"streamlabs-event_list",
|
|
7511
|
+
response.settings,
|
|
7512
|
+
CUSTOM_HTML_REVIEW_REASON
|
|
7513
|
+
)
|
|
7514
|
+
);
|
|
7515
|
+
}
|
|
7123
7516
|
} else if (PLACEHOLDER_WIDGET_TYPES.has(widgetType)) {
|
|
7124
7517
|
const placeholder = buildPlaceholder(widgetType);
|
|
7125
7518
|
const reason = PLACEHOLDER_REASONS[widgetType] ?? "No native Lumia mapping yet.";
|
|
@@ -7168,7 +7561,7 @@ function importStreamlabsWidget(response, widgetType) {
|
|
|
7168
7561
|
uuid: "",
|
|
7169
7562
|
listen_id: "",
|
|
7170
7563
|
name: overlayName,
|
|
7171
|
-
description:
|
|
7564
|
+
description: buildSLDescription(widgetType, options.sourceUrl),
|
|
7172
7565
|
settings: {
|
|
7173
7566
|
metadata: { width: 1920, height: 1080 },
|
|
7174
7567
|
layers,
|
|
@@ -8752,7 +9145,12 @@ function SEImportWizard({
|
|
|
8752
9145
|
);
|
|
8753
9146
|
}
|
|
8754
9147
|
importedOverlays.push({
|
|
8755
|
-
result: importSEBootstrap(bootstrap
|
|
9148
|
+
result: importSEBootstrap(bootstrap, {
|
|
9149
|
+
// JWT-flow imports don't come from a pasted URL; synthesize
|
|
9150
|
+
// the SE dashboard URL so the description still carries a
|
|
9151
|
+
// useful traceback. Same shape SE editor links use.
|
|
9152
|
+
sourceUrl: `https://streamelements.com/dashboard/overlays/${overlayId}/editor`
|
|
9153
|
+
}),
|
|
8756
9154
|
summary
|
|
8757
9155
|
});
|
|
8758
9156
|
}
|
|
@@ -8797,7 +9195,9 @@ function SEImportWizard({
|
|
|
8797
9195
|
"StreamElements returned a payload that doesn't look like an overlay bootstrap."
|
|
8798
9196
|
);
|
|
8799
9197
|
}
|
|
8800
|
-
const imported = importSEBootstrap(bootstrap
|
|
9198
|
+
const imported = importSEBootstrap(bootstrap, {
|
|
9199
|
+
sourceUrl: `https://streamelements.com/dashboard/overlays/${overlayId}/editor`
|
|
9200
|
+
});
|
|
8801
9201
|
setBatchImports([{ result: imported, summary }]);
|
|
8802
9202
|
setResult(imported);
|
|
8803
9203
|
setAssetUrls(findSEAssetURLs(imported.overlay));
|
|
@@ -8885,7 +9285,9 @@ function SEImportWizard({
|
|
|
8885
9285
|
"Streamlabs returned a response that does not look like a widget config payload."
|
|
8886
9286
|
);
|
|
8887
9287
|
}
|
|
8888
|
-
const imported = importStreamlabsWidget(raw, slParts.widgetType
|
|
9288
|
+
const imported = importStreamlabsWidget(raw, slParts.widgetType, {
|
|
9289
|
+
sourceUrl: url
|
|
9290
|
+
});
|
|
8889
9291
|
setBatchImports([]);
|
|
8890
9292
|
setResult(imported);
|
|
8891
9293
|
setAssetUrls(findSEAssetURLs(imported.overlay));
|
|
@@ -8926,7 +9328,7 @@ function SEImportWizard({
|
|
|
8926
9328
|
"StreamElements returned a response that does not look like an Element widget payload."
|
|
8927
9329
|
);
|
|
8928
9330
|
}
|
|
8929
|
-
const imported = importElementOverlay(raw);
|
|
9331
|
+
const imported = importElementOverlay(raw, { sourceUrl: url });
|
|
8930
9332
|
setBatchImports([]);
|
|
8931
9333
|
setResult(imported);
|
|
8932
9334
|
setAssetUrls(findSEAssetURLs(imported.overlay));
|
|
@@ -8974,7 +9376,7 @@ function SEImportWizard({
|
|
|
8974
9376
|
...parts,
|
|
8975
9377
|
proxyFetch: bindings.proxyFetch
|
|
8976
9378
|
});
|
|
8977
|
-
const imported = importSEBootstrap(bootstrap);
|
|
9379
|
+
const imported = importSEBootstrap(bootstrap, { sourceUrl: url });
|
|
8978
9380
|
setBatchImports([]);
|
|
8979
9381
|
setResult(imported);
|
|
8980
9382
|
setAssetUrls(findSEAssetURLs(imported.overlay));
|
|
@@ -9633,7 +10035,7 @@ function extractErrorMessage(err) {
|
|
|
9633
10035
|
}
|
|
9634
10036
|
|
|
9635
10037
|
// src/se-import/element-dispatch.ts
|
|
9636
|
-
import {
|
|
10038
|
+
import { LumiaAlertValues as LumiaAlertValues3 } from "@lumiastream/lumia-types";
|
|
9637
10039
|
|
|
9638
10040
|
// src/se-import/element-types.ts
|
|
9639
10041
|
function isSEElementResponse(value) {
|
|
@@ -9877,34 +10279,8 @@ function translateAnimationName(name, fallback) {
|
|
|
9877
10279
|
if (PINNED_ANIMATION_MAP[name]) return PINNED_ANIMATION_MAP[name];
|
|
9878
10280
|
return name.replace(/-([a-z])/g, (_m, c) => c.toUpperCase());
|
|
9879
10281
|
}
|
|
9880
|
-
|
|
9881
|
-
|
|
9882
|
-
if (typeof raw !== "string") return fallbackMs;
|
|
9883
|
-
const trimmed = raw.trim();
|
|
9884
|
-
if (!trimmed) return fallbackMs;
|
|
9885
|
-
if (trimmed.endsWith("ms")) {
|
|
9886
|
-
const n2 = parseFloat(trimmed.slice(0, -2));
|
|
9887
|
-
return Number.isFinite(n2) ? n2 : fallbackMs;
|
|
9888
|
-
}
|
|
9889
|
-
if (trimmed.endsWith("s")) {
|
|
9890
|
-
const n2 = parseFloat(trimmed.slice(0, -1));
|
|
9891
|
-
return Number.isFinite(n2) ? n2 * 1e3 : fallbackMs;
|
|
9892
|
-
}
|
|
9893
|
-
const n = parseFloat(trimmed);
|
|
9894
|
-
return Number.isFinite(n) ? n : fallbackMs;
|
|
9895
|
-
}
|
|
9896
|
-
function parsePx2(raw) {
|
|
9897
|
-
if (typeof raw === "number" && Number.isFinite(raw)) return raw;
|
|
9898
|
-
if (typeof raw !== "string") return NaN;
|
|
9899
|
-
const trimmed = raw.trim();
|
|
9900
|
-
if (!trimmed) return NaN;
|
|
9901
|
-
if (trimmed.endsWith("px")) {
|
|
9902
|
-
const n2 = parseFloat(trimmed.slice(0, -2));
|
|
9903
|
-
return Number.isFinite(n2) ? n2 : NaN;
|
|
9904
|
-
}
|
|
9905
|
-
const n = parseFloat(trimmed);
|
|
9906
|
-
return Number.isFinite(n) ? n : NaN;
|
|
9907
|
-
}
|
|
10282
|
+
var parseAnimationDurationMs = parseDurationMs;
|
|
10283
|
+
var parsePx2 = (raw) => pxToNumber(raw, NaN);
|
|
9908
10284
|
function findFieldByType(fields, type) {
|
|
9909
10285
|
if (!fields) return void 0;
|
|
9910
10286
|
for (const f of Object.values(fields)) {
|
|
@@ -9980,6 +10356,47 @@ function buildAnimations(title) {
|
|
|
9980
10356
|
const visibleDurationMs = enterDelay + enterDuration + staticDuration + exitDuration;
|
|
9981
10357
|
return { animations, visibleDurationMs: visibleDurationMs || DEFAULT_DURATION_MS };
|
|
9982
10358
|
}
|
|
10359
|
+
function resolveFieldBoundsInCanvas(style, canvas, textContent) {
|
|
10360
|
+
if (!style) return null;
|
|
10361
|
+
const x = parsePx2(style.left);
|
|
10362
|
+
const y = parsePx2(style.top);
|
|
10363
|
+
const hasXY = Number.isFinite(x) || Number.isFinite(y);
|
|
10364
|
+
if (!hasXY && style.width == null && style.height == null) return null;
|
|
10365
|
+
const sx = Number.isFinite(x) ? x : 0;
|
|
10366
|
+
const sy = Number.isFinite(y) ? y : 0;
|
|
10367
|
+
let width = parsePx2(style.width);
|
|
10368
|
+
let height = parsePx2(style.height);
|
|
10369
|
+
if (!Number.isFinite(width)) width = Math.max(1, canvas.width - sx);
|
|
10370
|
+
if (!Number.isFinite(height)) {
|
|
10371
|
+
if (typeof textContent === "string") {
|
|
10372
|
+
const lineCount = (textContent.match(/\n/g)?.length ?? 0) + 1;
|
|
10373
|
+
const lhPx = parsePx2(style.lineHeight) || parsePx2(style["line-height"]);
|
|
10374
|
+
const fsPx = parsePx2(style.fontSize);
|
|
10375
|
+
let effectiveLh = lhPx;
|
|
10376
|
+
if (!Number.isFinite(effectiveLh) || effectiveLh < 8) {
|
|
10377
|
+
effectiveLh = Number.isFinite(fsPx) ? fsPx * 1.3 : 24 * 1.3;
|
|
10378
|
+
}
|
|
10379
|
+
height = Math.max(1, lineCount * effectiveLh);
|
|
10380
|
+
} else {
|
|
10381
|
+
height = Math.max(1, canvas.height - sy);
|
|
10382
|
+
}
|
|
10383
|
+
}
|
|
10384
|
+
return { x: sx, y: sy, width, height };
|
|
10385
|
+
}
|
|
10386
|
+
function unionBounds(boxes) {
|
|
10387
|
+
if (boxes.length === 0) return null;
|
|
10388
|
+
let minX = Infinity;
|
|
10389
|
+
let minY = Infinity;
|
|
10390
|
+
let maxX = -Infinity;
|
|
10391
|
+
let maxY = -Infinity;
|
|
10392
|
+
for (const b of boxes) {
|
|
10393
|
+
minX = Math.min(minX, b.x);
|
|
10394
|
+
minY = Math.min(minY, b.y);
|
|
10395
|
+
maxX = Math.max(maxX, b.x + b.width);
|
|
10396
|
+
maxY = Math.max(maxY, b.y + b.height);
|
|
10397
|
+
}
|
|
10398
|
+
return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
|
|
10399
|
+
}
|
|
9983
10400
|
function buildElementAlertEvent(state, options = {}) {
|
|
9984
10401
|
const compositeFields = state.settings?.compositeFields;
|
|
9985
10402
|
const titleField = findFieldByType(compositeFields, "text");
|
|
@@ -9997,6 +10414,31 @@ function buildElementAlertEvent(state, options = {}) {
|
|
|
9997
10414
|
const titleStyle = titleField?.style;
|
|
9998
10415
|
const highlightColor = titleField?.highlightedStyle?.color ?? null;
|
|
9999
10416
|
const { animations, visibleDurationMs } = buildAnimations(titleField);
|
|
10417
|
+
const canvas = options.canvas ?? { width: 1920, height: 1080 };
|
|
10418
|
+
const mediaBounds = resolveFieldBoundsInCanvas(mainAsset?.style, canvas);
|
|
10419
|
+
const textBounds = resolveFieldBoundsInCanvas(titleStyle, canvas, rawMessage);
|
|
10420
|
+
const collected = [];
|
|
10421
|
+
if (mediaBounds) collected.push(mediaBounds);
|
|
10422
|
+
if (textBounds) collected.push(textBounds);
|
|
10423
|
+
let layerBounds = unionBounds(collected);
|
|
10424
|
+
if (!layerBounds) {
|
|
10425
|
+
layerBounds = {
|
|
10426
|
+
x: Math.round(canvas.width * 0.6),
|
|
10427
|
+
y: Math.round(canvas.height * 0.15),
|
|
10428
|
+
width: 400,
|
|
10429
|
+
height: 300
|
|
10430
|
+
};
|
|
10431
|
+
}
|
|
10432
|
+
let pushTextUp = 0;
|
|
10433
|
+
let pushTextLeft = 0;
|
|
10434
|
+
if (textBounds) {
|
|
10435
|
+
const desiredTextTopInLayer = textBounds.y - layerBounds.y;
|
|
10436
|
+
const naturalTextTopInLayer = Math.max(0, (layerBounds.height - textBounds.height) / 2);
|
|
10437
|
+
pushTextUp = Math.round(desiredTextTopInLayer - naturalTextTopInLayer);
|
|
10438
|
+
const desiredTextLeftInLayer = textBounds.x - layerBounds.x;
|
|
10439
|
+
const naturalTextLeftInLayer = Math.max(0, (layerBounds.width - textBounds.width) / 2);
|
|
10440
|
+
pushTextLeft = Math.round(desiredTextLeftInLayer - naturalTextLeftInLayer);
|
|
10441
|
+
}
|
|
10000
10442
|
const event = {
|
|
10001
10443
|
on: true,
|
|
10002
10444
|
// `'custom'` lowercase is load-bearing in Lumia's AlertBoxHandler —
|
|
@@ -10015,7 +10457,11 @@ function buildElementAlertEvent(state, options = {}) {
|
|
|
10015
10457
|
showBrandIcon: true
|
|
10016
10458
|
},
|
|
10017
10459
|
settings: { duration: visibleDurationMs },
|
|
10018
|
-
|
|
10460
|
+
// `textOver` matches SE Element's text-on-video render pattern.
|
|
10461
|
+
// `imageOver` / `column` / `row` would all stack the text outside
|
|
10462
|
+
// the media, which is the bug this layout fixes (text rendered
|
|
10463
|
+
// below the alert graphic instead of overlaid on it).
|
|
10464
|
+
layout: { value: "textOver", css: {}, customCss: "", pushTextUp, pushTextLeft },
|
|
10019
10465
|
text: {
|
|
10020
10466
|
css: titleCss(titleStyle),
|
|
10021
10467
|
messageCss: {},
|
|
@@ -10037,44 +10483,13 @@ function buildElementAlertEvent(state, options = {}) {
|
|
|
10037
10483
|
},
|
|
10038
10484
|
variations: []
|
|
10039
10485
|
};
|
|
10040
|
-
|
|
10041
|
-
let bounds;
|
|
10042
|
-
if (style) {
|
|
10043
|
-
const x = parsePx2(style.left);
|
|
10044
|
-
const y = parsePx2(style.top);
|
|
10045
|
-
const width = parsePx2(style.width);
|
|
10046
|
-
const height = parsePx2(style.height);
|
|
10047
|
-
if (Number.isFinite(x) && Number.isFinite(y) && Number.isFinite(width)) {
|
|
10048
|
-
bounds = {
|
|
10049
|
-
x,
|
|
10050
|
-
y,
|
|
10051
|
-
width,
|
|
10052
|
-
// Height isn't always present on video Main Assets (the video
|
|
10053
|
-
// aspect-ratios itself from the source). Fall back to a
|
|
10054
|
-
// reasonable default that matches SE's preview row.
|
|
10055
|
-
height: Number.isFinite(height) ? height : Math.round(width * 0.75)
|
|
10056
|
-
};
|
|
10057
|
-
}
|
|
10058
|
-
}
|
|
10059
|
-
return { event, bounds };
|
|
10486
|
+
return { event, bounds: layerBounds };
|
|
10060
10487
|
}
|
|
10061
10488
|
|
|
10062
10489
|
// src/se-import/mappers/element-scene.ts
|
|
10063
|
-
import { nanoid as nanoid8 } from "nanoid";
|
|
10064
10490
|
var TEXT_FALLBACK = { width: 400, height: 50 };
|
|
10065
10491
|
var IMAGE_FALLBACK = { width: 300, height: 100 };
|
|
10066
|
-
|
|
10067
|
-
if (typeof raw === "number" && Number.isFinite(raw)) return raw;
|
|
10068
|
-
if (typeof raw !== "string") return NaN;
|
|
10069
|
-
const trimmed = raw.trim();
|
|
10070
|
-
if (!trimmed) return NaN;
|
|
10071
|
-
if (trimmed.endsWith("px")) {
|
|
10072
|
-
const n2 = parseFloat(trimmed.slice(0, -2));
|
|
10073
|
-
return Number.isFinite(n2) ? n2 : NaN;
|
|
10074
|
-
}
|
|
10075
|
-
const n = parseFloat(trimmed);
|
|
10076
|
-
return Number.isFinite(n) ? n : NaN;
|
|
10077
|
-
}
|
|
10492
|
+
var parsePx3 = (raw) => pxToNumber(raw, NaN);
|
|
10078
10493
|
function styleToBounds(style, type, canvas) {
|
|
10079
10494
|
const x = Number.isFinite(parsePx3(style?.left)) ? parsePx3(style?.left) : 0;
|
|
10080
10495
|
const y = Number.isFinite(parsePx3(style?.top)) ? parsePx3(style?.top) : 0;
|
|
@@ -10110,33 +10525,17 @@ function styleToBounds(style, type, canvas) {
|
|
|
10110
10525
|
}
|
|
10111
10526
|
return { x, y, width, height };
|
|
10112
10527
|
}
|
|
10113
|
-
function
|
|
10114
|
-
|
|
10115
|
-
|
|
10116
|
-
|
|
10117
|
-
|
|
10118
|
-
bounds: {
|
|
10119
|
-
x: bounds.x,
|
|
10120
|
-
y: bounds.y,
|
|
10121
|
-
width: bounds.width,
|
|
10122
|
-
height: bounds.height,
|
|
10123
|
-
scale: [1, 1],
|
|
10124
|
-
zIndex
|
|
10125
|
-
}
|
|
10126
|
-
};
|
|
10127
|
-
}
|
|
10128
|
-
function buildModule(id, type, title, content, css = {}) {
|
|
10129
|
-
return {
|
|
10130
|
-
id,
|
|
10131
|
-
version: 1,
|
|
10132
|
-
loaded: true,
|
|
10133
|
-
settings: { type, title, locked: false },
|
|
10134
|
-
lights: [],
|
|
10135
|
-
css,
|
|
10528
|
+
function buildChild(groupId, zIndex, bounds, type, title, content, css = {}) {
|
|
10529
|
+
const { layer, module } = buildModuleEnvelope({
|
|
10530
|
+
type,
|
|
10531
|
+
title,
|
|
10532
|
+
bounds,
|
|
10136
10533
|
content,
|
|
10137
|
-
|
|
10138
|
-
|
|
10139
|
-
|
|
10534
|
+
css,
|
|
10535
|
+
parentGroupId: groupId,
|
|
10536
|
+
zIndex
|
|
10537
|
+
});
|
|
10538
|
+
return { layer, module };
|
|
10140
10539
|
}
|
|
10141
10540
|
function textStyleToCss(style) {
|
|
10142
10541
|
const s = style ?? {};
|
|
@@ -10151,80 +10550,69 @@ function textStyleToCss(style) {
|
|
|
10151
10550
|
return out;
|
|
10152
10551
|
}
|
|
10153
10552
|
function mapCompositeField(name, field, groupId, zIndex, provider, canvas) {
|
|
10154
|
-
const id = nanoid8();
|
|
10155
10553
|
switch (field.type) {
|
|
10156
10554
|
case "video": {
|
|
10157
|
-
const bounds = styleToBounds(field.style, "video", canvas);
|
|
10158
|
-
const layer = buildLayer(id, groupId, bounds, zIndex);
|
|
10159
10555
|
const src = pickFieldMediaHref(field.media, "video/");
|
|
10160
10556
|
const volume = typeof field.volume === "number" ? field.volume : 1;
|
|
10161
|
-
|
|
10162
|
-
|
|
10163
|
-
|
|
10164
|
-
|
|
10165
|
-
|
|
10166
|
-
|
|
10167
|
-
|
|
10168
|
-
|
|
10557
|
+
return buildChild(
|
|
10558
|
+
groupId,
|
|
10559
|
+
zIndex,
|
|
10560
|
+
styleToBounds(field.style, "video", canvas),
|
|
10561
|
+
"video",
|
|
10562
|
+
name,
|
|
10563
|
+
{ src, volume, loop: true, autoplay: true, muted: volume === 0 }
|
|
10564
|
+
);
|
|
10169
10565
|
}
|
|
10170
10566
|
case "image": {
|
|
10171
|
-
const bounds = styleToBounds(field.style, "image", canvas);
|
|
10172
|
-
const layer = buildLayer(id, groupId, bounds, zIndex);
|
|
10173
10567
|
const src = pickFieldMediaHref(field.media, "image/");
|
|
10174
|
-
|
|
10175
|
-
|
|
10568
|
+
return buildChild(
|
|
10569
|
+
groupId,
|
|
10570
|
+
zIndex,
|
|
10571
|
+
styleToBounds(field.style, "image", canvas),
|
|
10572
|
+
"image",
|
|
10573
|
+
name,
|
|
10574
|
+
{ src }
|
|
10575
|
+
);
|
|
10176
10576
|
}
|
|
10177
10577
|
case "text": {
|
|
10178
|
-
const bounds = styleToBounds(field.style, "text", canvas);
|
|
10179
|
-
const layer = buildLayer(id, groupId, bounds, zIndex);
|
|
10180
10578
|
const raw = field.text?.[0]?.content ?? "";
|
|
10181
10579
|
const value = substituteElementTokens(raw, { provider });
|
|
10182
|
-
|
|
10183
|
-
|
|
10580
|
+
return buildChild(
|
|
10581
|
+
groupId,
|
|
10582
|
+
zIndex,
|
|
10583
|
+
styleToBounds(field.style, "text", canvas),
|
|
10184
10584
|
"text",
|
|
10185
10585
|
name,
|
|
10186
10586
|
{ value, highlightColor: "inherit" },
|
|
10187
10587
|
textStyleToCss(field.style)
|
|
10188
10588
|
);
|
|
10189
|
-
return { layer, module };
|
|
10190
10589
|
}
|
|
10191
10590
|
case "audio": {
|
|
10192
|
-
const layer = buildLayer(id, groupId, { x: 0, y: 0, width: 1, height: 1 }, zIndex);
|
|
10193
10591
|
const src = pickFieldMediaHref(field.media, "audio/");
|
|
10194
10592
|
const volume = typeof field.volume === "number" ? field.volume : 0.5;
|
|
10195
|
-
|
|
10196
|
-
|
|
10593
|
+
return buildChild(
|
|
10594
|
+
groupId,
|
|
10595
|
+
zIndex,
|
|
10596
|
+
{ x: 0, y: 0, width: 1, height: 1 },
|
|
10597
|
+
"audio",
|
|
10598
|
+
name,
|
|
10599
|
+
{ src, volume, loop: false }
|
|
10600
|
+
);
|
|
10197
10601
|
}
|
|
10198
10602
|
default:
|
|
10199
10603
|
return null;
|
|
10200
10604
|
}
|
|
10201
10605
|
}
|
|
10202
10606
|
function buildElementScene(state, options) {
|
|
10203
|
-
const groupId = nanoid8();
|
|
10204
10607
|
const groupTitle = typeof state.displayName === "string" && state.displayName || state.referenceId || "Scene";
|
|
10205
|
-
const groupLayer = {
|
|
10206
|
-
id: groupId,
|
|
10608
|
+
const { id: groupId, layer: groupLayer, module: groupModule } = buildModuleEnvelope({
|
|
10207
10609
|
type: "group",
|
|
10208
|
-
|
|
10209
|
-
bounds: {
|
|
10210
|
-
|
|
10211
|
-
|
|
10212
|
-
|
|
10213
|
-
|
|
10214
|
-
scale: [1, 1]
|
|
10215
|
-
},
|
|
10216
|
-
state: { visible: true, locked: false, expanded: false }
|
|
10217
|
-
};
|
|
10218
|
-
const groupModule = {
|
|
10219
|
-
id: groupId,
|
|
10220
|
-
loaded: true,
|
|
10221
|
-
settings: { type: "group", title: groupTitle },
|
|
10222
|
-
lights: [],
|
|
10223
|
-
css: {},
|
|
10224
|
-
content: {},
|
|
10225
|
-
variables: {},
|
|
10226
|
-
events: {}
|
|
10227
|
-
};
|
|
10610
|
+
title: groupTitle,
|
|
10611
|
+
bounds: { x: 0, y: 0, width: options.canvas.width, height: options.canvas.height },
|
|
10612
|
+
type_layer: "group",
|
|
10613
|
+
parentGroupId: null,
|
|
10614
|
+
expanded: false
|
|
10615
|
+
});
|
|
10228
10616
|
const children = [];
|
|
10229
10617
|
const fields = state.settings?.compositeFields ?? {};
|
|
10230
10618
|
let zIndex = 1;
|
|
@@ -10243,7 +10631,12 @@ function buildElementScene(state, options) {
|
|
|
10243
10631
|
}
|
|
10244
10632
|
|
|
10245
10633
|
// src/se-import/element-dispatch.ts
|
|
10246
|
-
|
|
10634
|
+
function buildElementDescription(widgetInstanceId, sourceUrl) {
|
|
10635
|
+
return buildImportDescription(
|
|
10636
|
+
`Imported from StreamElements Element widget ${widgetInstanceId}`,
|
|
10637
|
+
sourceUrl
|
|
10638
|
+
);
|
|
10639
|
+
}
|
|
10247
10640
|
function elementAlertKindToLumiaKeys(kind, provider) {
|
|
10248
10641
|
switch (kind) {
|
|
10249
10642
|
case "follower":
|
|
@@ -10262,9 +10655,9 @@ function elementAlertKindToLumiaKeys(kind, provider) {
|
|
|
10262
10655
|
return alertBoxKeysFor("charityCampaignDonation", provider);
|
|
10263
10656
|
case "subscriberGift":
|
|
10264
10657
|
case "communityGiftPurchase": {
|
|
10265
|
-
if (provider === "twitch") return [
|
|
10266
|
-
if (provider === "kick") return [
|
|
10267
|
-
if (provider === "youtube") return [
|
|
10658
|
+
if (provider === "twitch") return [LumiaAlertValues3.TWITCH_GIFT_SUBSCRIPTION];
|
|
10659
|
+
if (provider === "kick") return [LumiaAlertValues3.KICK_GIFT_SUBSCRIPTION];
|
|
10660
|
+
if (provider === "youtube") return [LumiaAlertValues3.YOUTUBE_GIFT_MEMBERS];
|
|
10268
10661
|
return alertBoxKeysFor("subscriber", provider);
|
|
10269
10662
|
}
|
|
10270
10663
|
default:
|
|
@@ -10279,7 +10672,7 @@ function buildCombinedAlertModule(alertStates, provider, canvas) {
|
|
|
10279
10672
|
for (const state of alertStates) {
|
|
10280
10673
|
const cls = classifyConfigState(state);
|
|
10281
10674
|
if (cls.kind !== "alert") continue;
|
|
10282
|
-
const { event, bounds: bounds2 } = buildElementAlertEvent(state, { provider });
|
|
10675
|
+
const { event, bounds: bounds2 } = buildElementAlertEvent(state, { provider, canvas });
|
|
10283
10676
|
const keys = elementAlertKindToLumiaKeys(cls.alertKind, provider);
|
|
10284
10677
|
if (keys.length === 0) continue;
|
|
10285
10678
|
for (const key of keys) {
|
|
@@ -10291,61 +10684,42 @@ function buildCombinedAlertModule(alertStates, provider, canvas) {
|
|
|
10291
10684
|
}
|
|
10292
10685
|
}
|
|
10293
10686
|
if (Object.keys(events).length === 0) return null;
|
|
10294
|
-
const id = nanoid9();
|
|
10295
10687
|
const bounds = firstBounds ?? {
|
|
10296
10688
|
x: Math.round(canvas.width * 0.6),
|
|
10297
10689
|
y: Math.round(canvas.height * 0.15),
|
|
10298
10690
|
width: 400,
|
|
10299
10691
|
height: 300
|
|
10300
10692
|
};
|
|
10301
|
-
const layer = {
|
|
10302
|
-
id,
|
|
10303
|
-
state: { visible: true, locked: false },
|
|
10304
|
-
bounds: {
|
|
10305
|
-
x: bounds.x,
|
|
10306
|
-
y: bounds.y,
|
|
10307
|
-
width: bounds.width,
|
|
10308
|
-
height: bounds.height,
|
|
10309
|
-
scale: [1, 1]
|
|
10310
|
-
}
|
|
10311
|
-
};
|
|
10312
10693
|
const baseAlert = firstEvent ? stripEventOnlyFields2(firstEvent) : {};
|
|
10313
|
-
const module = {
|
|
10314
|
-
|
|
10315
|
-
|
|
10316
|
-
|
|
10317
|
-
settings: { type: "alert", title: "Alerts (Element import)", locked: false },
|
|
10318
|
-
lights: [],
|
|
10319
|
-
css: {},
|
|
10320
|
-
content: {},
|
|
10321
|
-
variables: {},
|
|
10322
|
-
events: {},
|
|
10694
|
+
const { layer, module } = buildModuleEnvelope({
|
|
10695
|
+
type: "alert",
|
|
10696
|
+
title: "Alerts (Element import)",
|
|
10697
|
+
bounds,
|
|
10323
10698
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
10324
10699
|
alert: { ...baseAlert, events }
|
|
10325
|
-
};
|
|
10700
|
+
});
|
|
10326
10701
|
return { layer, module };
|
|
10327
10702
|
}
|
|
10328
10703
|
function stripEventOnlyFields2(event) {
|
|
10329
|
-
const {
|
|
10704
|
+
const {
|
|
10705
|
+
on: _on,
|
|
10706
|
+
variations: _variations,
|
|
10707
|
+
matchEmptyCondition: _mec,
|
|
10708
|
+
...base
|
|
10709
|
+
} = event;
|
|
10330
10710
|
return base;
|
|
10331
10711
|
}
|
|
10332
10712
|
function stampImportMeta2(module, state, role) {
|
|
10333
|
-
module
|
|
10334
|
-
|
|
10335
|
-
|
|
10336
|
-
|
|
10337
|
-
|
|
10338
|
-
|
|
10339
|
-
|
|
10340
|
-
displayName: state?.displayName,
|
|
10341
|
-
importedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
10342
|
-
}
|
|
10343
|
-
};
|
|
10713
|
+
stampImportMetaMerge(module, {
|
|
10714
|
+
source: "streamelements-element",
|
|
10715
|
+
role,
|
|
10716
|
+
referenceId: state?.referenceId,
|
|
10717
|
+
categoryId: state?.categoryId,
|
|
10718
|
+
displayName: state?.displayName
|
|
10719
|
+
});
|
|
10344
10720
|
}
|
|
10345
|
-
function importElementOverlay(response) {
|
|
10346
|
-
const configData = parseElementConfigData(
|
|
10347
|
-
response.widgetInstanceConfigVersion.configData
|
|
10348
|
-
);
|
|
10721
|
+
function importElementOverlay(response, options = {}) {
|
|
10722
|
+
const configData = parseElementConfigData(response.widgetInstanceConfigVersion.configData);
|
|
10349
10723
|
if (!configData) {
|
|
10350
10724
|
throw new Error(
|
|
10351
10725
|
"StreamElements Element response carries an invalid configData payload. The widget may be corrupted or the response format may have changed."
|
|
@@ -10415,7 +10789,10 @@ function importElementOverlay(response) {
|
|
|
10415
10789
|
uuid: "",
|
|
10416
10790
|
listen_id: "",
|
|
10417
10791
|
name: response.widgetInstance.displayName ? `[SE Element] ${response.widgetInstance.displayName}` : `Imported from StreamElements Element (${response.widgetInstance.widgetInstanceId})`,
|
|
10418
|
-
description:
|
|
10792
|
+
description: buildElementDescription(
|
|
10793
|
+
response.widgetInstance.widgetInstanceId,
|
|
10794
|
+
options.sourceUrl
|
|
10795
|
+
),
|
|
10419
10796
|
settings: {
|
|
10420
10797
|
metadata: { width: canvas.width, height: canvas.height },
|
|
10421
10798
|
layers,
|
|
@@ -10429,7 +10806,7 @@ function importElementOverlay(response) {
|
|
|
10429
10806
|
}
|
|
10430
10807
|
|
|
10431
10808
|
// src/se-import/index.ts
|
|
10432
|
-
var
|
|
10809
|
+
var IMPORT_META_KEY2 = "importMeta";
|
|
10433
10810
|
var REVIEW_REASONS = {
|
|
10434
10811
|
"se-widget-bit-boss": "No native Lumia equivalent \u2014 bit boss fight bar with HP/damage states.",
|
|
10435
10812
|
// `se-widget-hype-cup` intentionally omitted — it now routes to the native
|
|
@@ -10459,6 +10836,10 @@ function reasonFor(seType, status, flaggedOff) {
|
|
|
10459
10836
|
if (flaggedOff && FLAG_OFF_REASONS[seType]) return FLAG_OFF_REASONS[seType];
|
|
10460
10837
|
return REVIEW_REASONS[seType] ?? (status === "template" ? "Imported as a generic template stub." : "No native mapping yet \u2014 imported as a labelled text placeholder.");
|
|
10461
10838
|
}
|
|
10839
|
+
function buildSEBootstrapDescription(overlayId, sourceUrl) {
|
|
10840
|
+
const head = overlayId ? `Imported from StreamElements overlay ${overlayId}` : "Imported from StreamElements";
|
|
10841
|
+
return buildImportDescription(head, sourceUrl);
|
|
10842
|
+
}
|
|
10462
10843
|
function extractSEOverlayId(input) {
|
|
10463
10844
|
const trimmed = input.trim();
|
|
10464
10845
|
const m = trimmed.match(/[0-9a-f]{24}/i);
|
|
@@ -10513,7 +10894,7 @@ function isSEBootstrap(value) {
|
|
|
10513
10894
|
const o = v.overlay;
|
|
10514
10895
|
return Array.isArray(o.widgets);
|
|
10515
10896
|
}
|
|
10516
|
-
function importSEBootstrap(bootstrap) {
|
|
10897
|
+
function importSEBootstrap(bootstrap, options = {}) {
|
|
10517
10898
|
const seOverlay = bootstrap.overlay;
|
|
10518
10899
|
const widgets = seOverlay?.widgets ?? [];
|
|
10519
10900
|
const coverage = {
|
|
@@ -10536,7 +10917,7 @@ function importSEBootstrap(bootstrap) {
|
|
|
10536
10917
|
if (w.type !== "se-widget-group") continue;
|
|
10537
10918
|
const uid = w.variables?.uid;
|
|
10538
10919
|
if (typeof uid !== "string") continue;
|
|
10539
|
-
const lumiaGroupId =
|
|
10920
|
+
const lumiaGroupId = nanoid8();
|
|
10540
10921
|
seUidToLumiaGroupId[uid] = lumiaGroupId;
|
|
10541
10922
|
groupWidgetByUid[uid] = w;
|
|
10542
10923
|
}
|
|
@@ -10575,7 +10956,7 @@ function importSEBootstrap(bootstrap) {
|
|
|
10575
10956
|
lights: [],
|
|
10576
10957
|
css: {},
|
|
10577
10958
|
content: {
|
|
10578
|
-
[
|
|
10959
|
+
[IMPORT_META_KEY2]: {
|
|
10579
10960
|
source: "streamelements",
|
|
10580
10961
|
widgetType: w.type,
|
|
10581
10962
|
importedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -10613,7 +10994,7 @@ function importSEBootstrap(bootstrap) {
|
|
|
10613
10994
|
if (result.status === "placeholder" || result.status === "template") {
|
|
10614
10995
|
module.content = {
|
|
10615
10996
|
...module.content ?? {},
|
|
10616
|
-
[
|
|
10997
|
+
[IMPORT_META_KEY2]: {
|
|
10617
10998
|
source: "streamelements",
|
|
10618
10999
|
widgetType: w.type,
|
|
10619
11000
|
importedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -10653,7 +11034,7 @@ function importSEBootstrap(bootstrap) {
|
|
|
10653
11034
|
uuid: "",
|
|
10654
11035
|
listen_id: "",
|
|
10655
11036
|
name: seOverlay?.name ? `[SE] ${seOverlay.name}` : `Imported from StreamElements (${seOverlay?._id ?? ""})`,
|
|
10656
|
-
description: seOverlay?._id
|
|
11037
|
+
description: buildSEBootstrapDescription(seOverlay?._id, options.sourceUrl),
|
|
10657
11038
|
settings: {
|
|
10658
11039
|
metadata: {
|
|
10659
11040
|
width: seOverlay?.settings?.width ?? 1920,
|
|
@@ -10701,7 +11082,7 @@ function applyReviewAction(result, moduleId, action, payload) {
|
|
|
10701
11082
|
bounds: existingLayer.bounds,
|
|
10702
11083
|
state: existingLayer.state
|
|
10703
11084
|
};
|
|
10704
|
-
const importMeta2 = existing2.content?.[
|
|
11085
|
+
const importMeta2 = existing2.content?.[IMPORT_META_KEY2];
|
|
10705
11086
|
const mergedLights = (existing2.lights?.length ?? 0) > 0 ? existing2.lights : transplant.module.lights;
|
|
10706
11087
|
const mergedEvents = existing2.events && Object.keys(existing2.events).length > 0 ? { ...transplant.module.events, ...existing2.events } : transplant.module.events;
|
|
10707
11088
|
const mergedVariables = existing2.variables && Object.keys(existing2.variables).length > 0 ? { ...transplant.module.variables, ...existing2.variables } : transplant.module.variables;
|
|
@@ -10713,7 +11094,7 @@ function applyReviewAction(result, moduleId, action, payload) {
|
|
|
10713
11094
|
variables: mergedVariables,
|
|
10714
11095
|
content: {
|
|
10715
11096
|
...transplant.module.content ?? {},
|
|
10716
|
-
[
|
|
11097
|
+
[IMPORT_META_KEY2]: importMeta2
|
|
10717
11098
|
}
|
|
10718
11099
|
};
|
|
10719
11100
|
const rootOldNewId = transplant.layer.id;
|
|
@@ -10741,7 +11122,7 @@ function applyReviewAction(result, moduleId, action, payload) {
|
|
|
10741
11122
|
const existing = modules[moduleId];
|
|
10742
11123
|
const generated = payload;
|
|
10743
11124
|
if (!existing || !generated) return result;
|
|
10744
|
-
const importMeta = existing.content?.[
|
|
11125
|
+
const importMeta = existing.content?.[IMPORT_META_KEY2];
|
|
10745
11126
|
const replaced = {
|
|
10746
11127
|
...existing,
|
|
10747
11128
|
settings: { ...existing.settings ?? {}, type: "custom" },
|
|
@@ -10755,7 +11136,7 @@ function applyReviewAction(result, moduleId, action, payload) {
|
|
|
10755
11136
|
data: parseLooseJson(generated.data) ?? {},
|
|
10756
11137
|
configs: parseLooseJson(generated.configs) ?? [],
|
|
10757
11138
|
flavor: "ai-generated",
|
|
10758
|
-
[
|
|
11139
|
+
[IMPORT_META_KEY2]: importMeta
|
|
10759
11140
|
}
|
|
10760
11141
|
};
|
|
10761
11142
|
modules[moduleId] = replaced;
|