@lumiastream/ui 0.6.1 → 0.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +66 -35
- package/dist/se-import.d.ts +2 -2
- package/dist/se-import.js +66 -35
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -4901,6 +4901,29 @@ function listenersToLumiaEvents(listeners, listener) {
|
|
|
4901
4901
|
}
|
|
4902
4902
|
return [...out];
|
|
4903
4903
|
}
|
|
4904
|
+
var SE_FIRST_PARTY_SCRIPT_RX = /^(?:https?:)?\/\/(?:[^/]+\.)?(?:streamelements\.com|lumiastream\.com)\//i;
|
|
4905
|
+
var EXTERNAL_SCRIPT_SRC_RX = /<script\b[^>]*\bsrc\s*=\s*["']([^"']+)["']/gi;
|
|
4906
|
+
var CHAT_COMMAND_VALUE_RX = /^\s*!\S+/;
|
|
4907
|
+
function hasExternalScriptLoader(html, js) {
|
|
4908
|
+
const haystack = `${html ?? ""}
|
|
4909
|
+
${js ?? ""}`;
|
|
4910
|
+
EXTERNAL_SCRIPT_SRC_RX.lastIndex = 0;
|
|
4911
|
+
for (const m of haystack.matchAll(EXTERNAL_SCRIPT_SRC_RX)) {
|
|
4912
|
+
const src = m[1]?.trim();
|
|
4913
|
+
if (!src) continue;
|
|
4914
|
+
if (src.startsWith("/") && !src.startsWith("//")) continue;
|
|
4915
|
+
if (SE_FIRST_PARTY_SCRIPT_RX.test(src)) continue;
|
|
4916
|
+
return true;
|
|
4917
|
+
}
|
|
4918
|
+
return false;
|
|
4919
|
+
}
|
|
4920
|
+
function hasChatCommandFieldValue(fieldData) {
|
|
4921
|
+
if (!fieldData) return false;
|
|
4922
|
+
for (const v of Object.values(fieldData)) {
|
|
4923
|
+
if (typeof v === "string" && CHAT_COMMAND_VALUE_RX.test(v)) return true;
|
|
4924
|
+
}
|
|
4925
|
+
return false;
|
|
4926
|
+
}
|
|
4904
4927
|
function translateConfigs(sourceFields) {
|
|
4905
4928
|
if (!sourceFields) return [];
|
|
4906
4929
|
return Object.entries(sourceFields).filter(([key]) => !RESERVED_FIELD_KEYS.has(key)).map(([key, f]) => {
|
|
@@ -4953,6 +4976,13 @@ function mapCustom(widget, ctx) {
|
|
|
4953
4976
|
widget.listeners,
|
|
4954
4977
|
widget.listener
|
|
4955
4978
|
);
|
|
4979
|
+
const externalLoader = hasExternalScriptLoader(v.html, v.js);
|
|
4980
|
+
const wantsChat = externalLoader || hasChatCommandFieldValue(fieldData);
|
|
4981
|
+
if (externalLoader) {
|
|
4982
|
+
if (!eventsList.includes("alert")) eventsList.push("alert");
|
|
4983
|
+
if (!eventsList.includes("alert.trigger")) eventsList.push("alert.trigger");
|
|
4984
|
+
}
|
|
4985
|
+
if (wantsChat && !eventsList.includes("chat")) eventsList.push("chat");
|
|
4956
4986
|
const data = { ...fieldData ?? {} };
|
|
4957
4987
|
if (eventsList.length > 0) data.events = eventsList;
|
|
4958
4988
|
return buildUnit(
|
|
@@ -6453,7 +6483,7 @@ function mapHypeCup(widget, ctx) {
|
|
|
6453
6483
|
// Phase 1 ships a single preset; SE's per-cup polygon set isn't lifted
|
|
6454
6484
|
// yet (it'd require multi-cup support which we deliberately deferred).
|
|
6455
6485
|
// The streamer can swap a custom cup image + wireframe via Settings.
|
|
6456
|
-
preset: "
|
|
6486
|
+
preset: "cup-01",
|
|
6457
6487
|
customCup: null,
|
|
6458
6488
|
cup: {
|
|
6459
6489
|
x: cupX,
|
|
@@ -6468,7 +6498,7 @@ function mapHypeCup(widget, ctx) {
|
|
|
6468
6498
|
maxBodies
|
|
6469
6499
|
},
|
|
6470
6500
|
tokens: {
|
|
6471
|
-
defaultRadius:
|
|
6501
|
+
defaultRadius: 16
|
|
6472
6502
|
},
|
|
6473
6503
|
events: categoriesFromListeners(widget.listeners),
|
|
6474
6504
|
chat: {
|
|
@@ -6477,6 +6507,7 @@ function mapHypeCup(widget, ctx) {
|
|
|
6477
6507
|
display: {
|
|
6478
6508
|
showWireframes: !!v.showWireframes,
|
|
6479
6509
|
showEventMessages: !!v.textEnabled,
|
|
6510
|
+
showPlatformIcons: false,
|
|
6480
6511
|
messageDuration: duration
|
|
6481
6512
|
},
|
|
6482
6513
|
sprites,
|
|
@@ -7080,7 +7111,7 @@ async function mirrorOneAsset(url, upload, signal, proxyAssetFetch) {
|
|
|
7080
7111
|
const blob = await fetchAssetBlob(url, signal, proxyAssetFetch);
|
|
7081
7112
|
if (signal?.aborted) throw new DOMException("Aborted", "AbortError");
|
|
7082
7113
|
if (blob.size > MAX_MIRROR_ASSET_BYTES) {
|
|
7083
|
-
throw new Error(`Asset is ${(blob.size / (1024 * 1024)).toFixed(1)} MB
|
|
7114
|
+
throw new Error(`Asset is ${(blob.size / (1024 * 1024)).toFixed(1)} MB, over the ${MAX_MIRROR_ASSET_BYTES / (1024 * 1024)} MB import cap.`);
|
|
7084
7115
|
}
|
|
7085
7116
|
const filename = filenameFromURL(url);
|
|
7086
7117
|
const type = blob.type || guessMime(filename);
|
|
@@ -7102,7 +7133,7 @@ async function fetchAssetBlob(url, signal, proxyAssetFetch) {
|
|
|
7102
7133
|
if (!res.ok) throw new Error(`Source returned HTTP ${res.status}`);
|
|
7103
7134
|
const declared = Number(res.headers.get("content-length"));
|
|
7104
7135
|
if (Number.isFinite(declared) && declared > MAX_MIRROR_ASSET_BYTES) {
|
|
7105
|
-
throw new Error(`Asset is ${(declared / (1024 * 1024)).toFixed(1)} MB
|
|
7136
|
+
throw new Error(`Asset is ${(declared / (1024 * 1024)).toFixed(1)} MB, over the ${MAX_MIRROR_ASSET_BYTES / (1024 * 1024)} MB import cap.`);
|
|
7106
7137
|
}
|
|
7107
7138
|
return res.blob();
|
|
7108
7139
|
}
|
|
@@ -7342,7 +7373,7 @@ function decodeJwtPayload(jwt) {
|
|
|
7342
7373
|
}
|
|
7343
7374
|
const claims = parsed;
|
|
7344
7375
|
if (typeof claims.channel !== "string" || typeof claims.authToken !== "string") {
|
|
7345
|
-
throw new SEAuthError("invalid-jwt", "Token payload is missing the 'channel' or 'authToken' claim
|
|
7376
|
+
throw new SEAuthError("invalid-jwt", "Token payload is missing the 'channel' or 'authToken' claim. not a StreamElements account token.");
|
|
7346
7377
|
}
|
|
7347
7378
|
if (typeof claims.exp === "number" && claims.exp * 1e3 < Date.now()) {
|
|
7348
7379
|
throw new SEAuthError("expired", "This StreamElements token has expired. Generate a new one on the SE Account page.");
|
|
@@ -7351,7 +7382,7 @@ function decodeJwtPayload(jwt) {
|
|
|
7351
7382
|
}
|
|
7352
7383
|
var SEClient = class _SEClient {
|
|
7353
7384
|
// `jwt` is held alongside the decoded claims so the client can switch auth
|
|
7354
|
-
// modes without re-parsing. Defaults to `bearer-jwt
|
|
7385
|
+
// modes without re-parsing. Defaults to `bearer-jwt`. confirmed against
|
|
7355
7386
|
// the user's diagnostic 2026-05-16: every endpoint the existing 5 helpers
|
|
7356
7387
|
// hit (users/current, channels/me, overlays, bot/commands, plus
|
|
7357
7388
|
// sessions/loyalty/bits leaderboard transitively) returns 200, AND
|
|
@@ -7439,7 +7470,7 @@ var SEClient = class _SEClient {
|
|
|
7439
7470
|
let detail = "";
|
|
7440
7471
|
try {
|
|
7441
7472
|
const body = await res.json();
|
|
7442
|
-
if (body?.message) detail = `
|
|
7473
|
+
if (body?.message) detail = ` - ${body.message}`;
|
|
7443
7474
|
} catch {
|
|
7444
7475
|
}
|
|
7445
7476
|
throw new Error(`StreamElements API returned HTTP ${res.status}${detail}`);
|
|
@@ -7481,7 +7512,7 @@ async function fetchElementWidgetInstancePublic(channelId, widgetInstanceId, pub
|
|
|
7481
7512
|
let detail = "";
|
|
7482
7513
|
try {
|
|
7483
7514
|
const body = JSON.parse(await result.errorBody());
|
|
7484
|
-
if (body?.message) detail = `
|
|
7515
|
+
if (body?.message) detail = ` - ${body.message}`;
|
|
7485
7516
|
} catch {
|
|
7486
7517
|
}
|
|
7487
7518
|
const reason = result.status === 401 || result.status === 403 ? "StreamElements rejected the widget share token. The share link may have been revoked or made private." : result.status === 404 ? "Element widget not found. Double-check the share URL." : `HTTP ${result.status}`;
|
|
@@ -8489,15 +8520,15 @@ var PLACEHOLDER_WIDGET_TYPES = /* @__PURE__ */ new Set([
|
|
|
8489
8520
|
"streamlabels"
|
|
8490
8521
|
]);
|
|
8491
8522
|
var PLACEHOLDER_REASONS = {
|
|
8492
|
-
donation_ticker: "No native Lumia equivalent yet
|
|
8493
|
-
stream_boss: "Lumia has a native streamboss module
|
|
8494
|
-
credits: "Lumia has a native credits module
|
|
8495
|
-
spin_wheel: "Lumia has a native spinwheel module
|
|
8496
|
-
emote_wall: "Lumia has a native emotebox module
|
|
8497
|
-
sub_train: "No native Lumia equivalent yet
|
|
8523
|
+
donation_ticker: "No native Lumia equivalent yet. imported as a placeholder text layer.",
|
|
8524
|
+
stream_boss: "Lumia has a native streamboss module. set it up directly rather than importing the SL config.",
|
|
8525
|
+
credits: "Lumia has a native credits module. set it up directly rather than importing the SL config.",
|
|
8526
|
+
spin_wheel: "Lumia has a native spinwheel module. set it up directly rather than importing the SL config.",
|
|
8527
|
+
emote_wall: "Lumia has a native emotebox module. set it up directly rather than importing the SL config.",
|
|
8528
|
+
sub_train: "No native Lumia equivalent yet. imported as a placeholder text layer.",
|
|
8498
8529
|
streamlabels: "Use Lumia template variables (e.g. {{twitch_last_follower}}) inside a Text module instead of importing the SL streamlabels config."
|
|
8499
8530
|
};
|
|
8500
|
-
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
|
|
8531
|
+
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. recreate the styling there. The original SL settings are stashed under module.content.importMeta for reference.";
|
|
8501
8532
|
function stampImportMeta(module, widgetType, role, extra = {}) {
|
|
8502
8533
|
stampImportMetaMerge(module, {
|
|
8503
8534
|
source: "streamlabs",
|
|
@@ -8655,7 +8686,7 @@ function importStreamlabsWidget(response, widgetType, options = {}) {
|
|
|
8655
8686
|
});
|
|
8656
8687
|
reviewItems.push({
|
|
8657
8688
|
moduleId: placeholder.module.id,
|
|
8658
|
-
// SL doesn't have a single "widget" object the way SE does
|
|
8689
|
+
// SL doesn't have a single "widget" object the way SE does. we
|
|
8659
8690
|
// stash the settings snapshot here so the review step can show
|
|
8660
8691
|
// the raw config the user imported. Cast through unknown because
|
|
8661
8692
|
// ReviewItem is currently typed against SEWidget; widening the
|
|
@@ -8673,7 +8704,7 @@ function importStreamlabsWidget(response, widgetType, options = {}) {
|
|
|
8673
8704
|
layers.push(placeholder.layer);
|
|
8674
8705
|
modules[placeholder.layer.id] = placeholder.module;
|
|
8675
8706
|
coverage.notes.push(
|
|
8676
|
-
`Unknown Streamlabs widget type "${widgetType}"
|
|
8707
|
+
`Unknown Streamlabs widget type "${widgetType}". imported as a placeholder.`
|
|
8677
8708
|
);
|
|
8678
8709
|
coverage.mappings.push({
|
|
8679
8710
|
seType: `streamlabs:${widgetType}`,
|
|
@@ -9086,7 +9117,7 @@ function StepHeader({
|
|
|
9086
9117
|
] });
|
|
9087
9118
|
}
|
|
9088
9119
|
var JWT_FEATURES = [
|
|
9089
|
-
"All your overlays
|
|
9120
|
+
"All your overlays: pick one, many, or all",
|
|
9090
9121
|
"Live counters (follower / sub / tip / bits goals)",
|
|
9091
9122
|
"Chat bot commands",
|
|
9092
9123
|
"Loyalty points + watch time",
|
|
@@ -9240,7 +9271,7 @@ function StepModePicker({
|
|
|
9240
9271
|
}
|
|
9241
9272
|
)
|
|
9242
9273
|
] }),
|
|
9243
|
-
caveat: "Requires pasting your StreamElements JWT. Stored only in memory
|
|
9274
|
+
caveat: "Requires pasting your StreamElements JWT. Stored only in memory. Never sent to Lumia.",
|
|
9244
9275
|
onClick: onChooseJwt
|
|
9245
9276
|
}
|
|
9246
9277
|
)
|
|
@@ -9265,7 +9296,7 @@ function StepConnect({
|
|
|
9265
9296
|
{
|
|
9266
9297
|
number: 1,
|
|
9267
9298
|
title: "Connect your StreamElements account",
|
|
9268
|
-
subtitle: "Paste your StreamElements JWT to import overlays, live counters, chat commands, loyalty points, watch time, and recent events. The token is read in your browser only
|
|
9299
|
+
subtitle: "Paste your StreamElements JWT to import overlays, live counters, chat commands, loyalty points, watch time, and recent events. The token is read in your browser only. Lumia never sends it to our servers."
|
|
9269
9300
|
}
|
|
9270
9301
|
),
|
|
9271
9302
|
/* @__PURE__ */ jsx14(
|
|
@@ -11914,15 +11945,15 @@ function importElementOverlay(response, options = {}) {
|
|
|
11914
11945
|
// src/se-import/index.ts
|
|
11915
11946
|
var IMPORT_META_KEY2 = "importMeta";
|
|
11916
11947
|
var REVIEW_REASONS = {
|
|
11917
|
-
"se-widget-bit-boss": "No native Lumia equivalent
|
|
11948
|
+
"se-widget-bit-boss": "No native Lumia equivalent. bit boss fight bar with HP/damage states.",
|
|
11918
11949
|
// `se-widget-hype-cup` intentionally omitted — it now routes to the native
|
|
11919
11950
|
// `tipjar` module (see dispatcher.ts) and lands with `partial` status, so it
|
|
11920
11951
|
// never reaches the placeholder-reason fallback. If a future feature flag
|
|
11921
11952
|
// disables tipjar auto-import, add an entry to FLAG_OFF_REASONS instead.
|
|
11922
|
-
"se-widget-media-share": "No native Lumia equivalent
|
|
11923
|
-
"se-widget-contest": "No native Lumia equivalent
|
|
11924
|
-
"se-widget-giveaway": "No native Lumia equivalent
|
|
11925
|
-
"se-widget-botcounter": "No native Lumia equivalent
|
|
11953
|
+
"se-widget-media-share": "No native Lumia equivalent. viewer-submitted media player.",
|
|
11954
|
+
"se-widget-contest": "No native Lumia equivalent. SE-specific contest widget.",
|
|
11955
|
+
"se-widget-giveaway": "No native Lumia equivalent. SE giveaway service.",
|
|
11956
|
+
"se-widget-botcounter": "No native Lumia equivalent. chatter/message counter.",
|
|
11926
11957
|
"se-widget-top-cheerers-list": 'Lumia loyaltyleaderboard needs a "source" extension before it can render cheer rankings.',
|
|
11927
11958
|
"se-widget-top-gifters-list": 'Lumia loyaltyleaderboard needs a "source" extension before it can render gifter rankings.',
|
|
11928
11959
|
"se-widget-top-tippers-list": 'Lumia loyaltyleaderboard needs a "source" extension before it can render tipper rankings.',
|
|
@@ -11932,7 +11963,7 @@ var REVIEW_REASONS = {
|
|
|
11932
11963
|
// so we can't pre-populate the slideshow items. The streamer adds their
|
|
11933
11964
|
// product images by hand in the slideshow editor. Status = 'partial' (see
|
|
11934
11965
|
// dispatcher.ts), so this string surfaces in the review step.
|
|
11935
|
-
"se-widget-merch-products-rotator": "Imported as an empty slideshow. Lumia doesn't fetch your Fourthwall product catalog yet, so add your product images manually in the slideshow editor
|
|
11966
|
+
"se-widget-merch-products-rotator": "Imported as an empty slideshow. Lumia doesn't fetch your Fourthwall product catalog yet, so add your product images manually in the slideshow editor. the layer is pre-positioned and styled to match your SE widget."
|
|
11936
11967
|
// Seasonal widgets (snow / halloween / halloween-2019 / xmas / valentine / easter
|
|
11937
11968
|
// / world-cup) intentionally omitted — they route directly to image/slideshow/
|
|
11938
11969
|
// video modules now (see dispatcher.ts SEASONAL_IMAGE_TYPES), so they always
|
|
@@ -11940,7 +11971,7 @@ var REVIEW_REASONS = {
|
|
|
11940
11971
|
};
|
|
11941
11972
|
function reasonFor(seType, status, flaggedOff) {
|
|
11942
11973
|
if (flaggedOff && FLAG_OFF_REASONS[seType]) return FLAG_OFF_REASONS[seType];
|
|
11943
|
-
return REVIEW_REASONS[seType] ?? (status === "template" ? "Imported as a generic template stub." : "No native mapping yet
|
|
11974
|
+
return REVIEW_REASONS[seType] ?? (status === "template" ? "Imported as a generic template stub." : "No native mapping yet. imported as a labelled text placeholder.");
|
|
11944
11975
|
}
|
|
11945
11976
|
function buildSEBootstrapDescription(overlayId, sourceUrl) {
|
|
11946
11977
|
const head = overlayId ? `Imported from StreamElements overlay ${overlayId}` : "Imported from StreamElements";
|
|
@@ -11970,7 +12001,7 @@ async function fetchSEBootstrap({
|
|
|
11970
12001
|
}) {
|
|
11971
12002
|
if (!apikey) {
|
|
11972
12003
|
throw new Error(
|
|
11973
|
-
"Missing access token. Use the public preview URL (Overlay menu \u2192 Get URL \u2192 Public link)
|
|
12004
|
+
"Missing access token. Use the public preview URL (Overlay menu \u2192 Get URL \u2192 Public link). editor URLs do not include a token."
|
|
11974
12005
|
);
|
|
11975
12006
|
}
|
|
11976
12007
|
const result = await fetchWithProxyFallback(buildBootstrapUrl(overlayId), {
|
|
@@ -11981,7 +12012,7 @@ async function fetchSEBootstrap({
|
|
|
11981
12012
|
let detail = "";
|
|
11982
12013
|
try {
|
|
11983
12014
|
const body = JSON.parse(await result.errorBody());
|
|
11984
|
-
detail = body?.message ?
|
|
12015
|
+
detail = body?.message ? `. ${body.message}` : "";
|
|
11985
12016
|
} catch {
|
|
11986
12017
|
}
|
|
11987
12018
|
const reason = result.status === 401 || result.status === 403 ? "StreamElements rejected the URL token. The preview link may have been revoked." : result.status === 404 ? "Overlay not found. Double-check the URL." : `HTTP ${result.status}`;
|
|
@@ -12123,17 +12154,17 @@ function importSEBootstrap(bootstrap, options = {}) {
|
|
|
12123
12154
|
}
|
|
12124
12155
|
if (coverage.mappings.some((m) => m.status === "placeholder")) {
|
|
12125
12156
|
coverage.notes.push(
|
|
12126
|
-
"Some widgets imported as placeholder text layers
|
|
12157
|
+
"Some widgets imported as placeholder text layers. see SE/05-widget-to-module-mapping.md for status."
|
|
12127
12158
|
);
|
|
12128
12159
|
}
|
|
12129
12160
|
if (coverage.mappings.some((m) => m.seType === "se-widget-merch-goal")) {
|
|
12130
12161
|
coverage.notes.push(
|
|
12131
|
-
"Merch-goal widgets imported as generic goals
|
|
12162
|
+
"Merch-goal widgets imported as generic goals. see SE/03-missing-variables.md \xA78 for the missing data source."
|
|
12132
12163
|
);
|
|
12133
12164
|
}
|
|
12134
12165
|
if (coverage.mappings.some((m) => m.seType === "se-widget-custom-event-list")) {
|
|
12135
12166
|
coverage.notes.push(
|
|
12136
|
-
"\u26A0 Custom widgets are experimental
|
|
12167
|
+
"\u26A0 Custom widgets are experimental. 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."
|
|
12137
12168
|
);
|
|
12138
12169
|
}
|
|
12139
12170
|
const overlay = {
|
|
@@ -12278,7 +12309,7 @@ function buildAIPromptForSEWidget(widget, canvas) {
|
|
|
12278
12309
|
const fullVariables = JSON.stringify(variables, null, 2);
|
|
12279
12310
|
const truncated = fullVariables.length > VARIABLES_BUDGET;
|
|
12280
12311
|
const variablesJson = truncated ? `${fullVariables.slice(0, VARIABLES_BUDGET)}
|
|
12281
|
-
\u2026[truncated
|
|
12312
|
+
\u2026[truncated. ${fullVariables.length - VARIABLES_BUDGET} more bytes elided]` : fullVariables;
|
|
12282
12313
|
return [
|
|
12283
12314
|
"Recreate a StreamElements overlay widget as a Lumia Stream custom overlay (HTML + CSS + JS).",
|
|
12284
12315
|
"",
|
|
@@ -12286,14 +12317,14 @@ function buildAIPromptForSEWidget(widget, canvas) {
|
|
|
12286
12317
|
listener ? `Primary event listener: ${listener}` : "",
|
|
12287
12318
|
listeners ? `Subscribes to events: ${listeners.join(", ")}` : "",
|
|
12288
12319
|
`Canvas: ${landing.width} \xD7 ${landing.height}`,
|
|
12289
|
-
truncated ? "Note: the widget config below is truncated
|
|
12320
|
+
truncated ? "Note: the widget config below is truncated. focus on visible/structural fields and ignore tail content you cannot see." : "",
|
|
12290
12321
|
"",
|
|
12291
12322
|
"Widget configuration (JSON):",
|
|
12292
12323
|
"```json",
|
|
12293
12324
|
variablesJson,
|
|
12294
12325
|
"```",
|
|
12295
12326
|
"",
|
|
12296
|
-
"Constraints
|
|
12327
|
+
"Constraints. your output must run inside a Lumia Custom Overlay sandboxed iframe:",
|
|
12297
12328
|
'- Use `window.Overlay.on("alert", handler)` / `window.Overlay.on("chat", handler)` to receive Lumia events.',
|
|
12298
12329
|
"- Lumia template variables use double-brace syntax: {{username}}, {{amount}}, {{message}}, {{currency}}.",
|
|
12299
12330
|
"- The `window.DATA` object holds user-configurable values (mirrors your `configs`).",
|
package/dist/se-import.d.ts
CHANGED
|
@@ -183,7 +183,7 @@ declare function fetchWithProxyFallback(url: string, options?: FetchWithProxyOpt
|
|
|
183
183
|
|
|
184
184
|
/**
|
|
185
185
|
* Distinct importable CDN URLs referenced anywhere inside the overlay's
|
|
186
|
-
* settings. Covers both StreamElements and Streamlabs source hosts
|
|
186
|
+
* settings. Covers both StreamElements and Streamlabs source hosts, see
|
|
187
187
|
* IMPORTABLE_CDN_REGEX above for the full host list.
|
|
188
188
|
*
|
|
189
189
|
* Name kept as `findSEAssetURLs` for back-compat with existing callers; the
|
|
@@ -211,7 +211,7 @@ interface AssetMirrorProgress {
|
|
|
211
211
|
*
|
|
212
212
|
* The host call site is responsible for retries and progress reporting;
|
|
213
213
|
* this function is intentionally single-shot. Pass `signal` (AbortSignal)
|
|
214
|
-
* to abort the source-side `fetch` when the modal closes mid-mirror
|
|
214
|
+
* to abort the source-side `fetch` when the modal closes mid-mirror, the
|
|
215
215
|
* upload itself is consumer-defined and the caller decides whether/how to
|
|
216
216
|
* short-circuit it. The proxy path doesn't take a signal because the
|
|
217
217
|
* host-provided fetch shape varies.
|
package/dist/se-import.js
CHANGED
|
@@ -1821,6 +1821,29 @@ function listenersToLumiaEvents(listeners, listener) {
|
|
|
1821
1821
|
}
|
|
1822
1822
|
return [...out];
|
|
1823
1823
|
}
|
|
1824
|
+
var SE_FIRST_PARTY_SCRIPT_RX = /^(?:https?:)?\/\/(?:[^/]+\.)?(?:streamelements\.com|lumiastream\.com)\//i;
|
|
1825
|
+
var EXTERNAL_SCRIPT_SRC_RX = /<script\b[^>]*\bsrc\s*=\s*["']([^"']+)["']/gi;
|
|
1826
|
+
var CHAT_COMMAND_VALUE_RX = /^\s*!\S+/;
|
|
1827
|
+
function hasExternalScriptLoader(html, js) {
|
|
1828
|
+
const haystack = `${html ?? ""}
|
|
1829
|
+
${js ?? ""}`;
|
|
1830
|
+
EXTERNAL_SCRIPT_SRC_RX.lastIndex = 0;
|
|
1831
|
+
for (const m of haystack.matchAll(EXTERNAL_SCRIPT_SRC_RX)) {
|
|
1832
|
+
const src = m[1]?.trim();
|
|
1833
|
+
if (!src) continue;
|
|
1834
|
+
if (src.startsWith("/") && !src.startsWith("//")) continue;
|
|
1835
|
+
if (SE_FIRST_PARTY_SCRIPT_RX.test(src)) continue;
|
|
1836
|
+
return true;
|
|
1837
|
+
}
|
|
1838
|
+
return false;
|
|
1839
|
+
}
|
|
1840
|
+
function hasChatCommandFieldValue(fieldData) {
|
|
1841
|
+
if (!fieldData) return false;
|
|
1842
|
+
for (const v of Object.values(fieldData)) {
|
|
1843
|
+
if (typeof v === "string" && CHAT_COMMAND_VALUE_RX.test(v)) return true;
|
|
1844
|
+
}
|
|
1845
|
+
return false;
|
|
1846
|
+
}
|
|
1824
1847
|
function translateConfigs(sourceFields) {
|
|
1825
1848
|
if (!sourceFields) return [];
|
|
1826
1849
|
return Object.entries(sourceFields).filter(([key]) => !RESERVED_FIELD_KEYS.has(key)).map(([key, f]) => {
|
|
@@ -1873,6 +1896,13 @@ function mapCustom(widget, ctx) {
|
|
|
1873
1896
|
widget.listeners,
|
|
1874
1897
|
widget.listener
|
|
1875
1898
|
);
|
|
1899
|
+
const externalLoader = hasExternalScriptLoader(v.html, v.js);
|
|
1900
|
+
const wantsChat = externalLoader || hasChatCommandFieldValue(fieldData);
|
|
1901
|
+
if (externalLoader) {
|
|
1902
|
+
if (!eventsList.includes("alert")) eventsList.push("alert");
|
|
1903
|
+
if (!eventsList.includes("alert.trigger")) eventsList.push("alert.trigger");
|
|
1904
|
+
}
|
|
1905
|
+
if (wantsChat && !eventsList.includes("chat")) eventsList.push("chat");
|
|
1876
1906
|
const data = { ...fieldData ?? {} };
|
|
1877
1907
|
if (eventsList.length > 0) data.events = eventsList;
|
|
1878
1908
|
return buildUnit(
|
|
@@ -3373,7 +3403,7 @@ function mapHypeCup(widget, ctx) {
|
|
|
3373
3403
|
// Phase 1 ships a single preset; SE's per-cup polygon set isn't lifted
|
|
3374
3404
|
// yet (it'd require multi-cup support which we deliberately deferred).
|
|
3375
3405
|
// The streamer can swap a custom cup image + wireframe via Settings.
|
|
3376
|
-
preset: "
|
|
3406
|
+
preset: "cup-01",
|
|
3377
3407
|
customCup: null,
|
|
3378
3408
|
cup: {
|
|
3379
3409
|
x: cupX,
|
|
@@ -3388,7 +3418,7 @@ function mapHypeCup(widget, ctx) {
|
|
|
3388
3418
|
maxBodies
|
|
3389
3419
|
},
|
|
3390
3420
|
tokens: {
|
|
3391
|
-
defaultRadius:
|
|
3421
|
+
defaultRadius: 16
|
|
3392
3422
|
},
|
|
3393
3423
|
events: categoriesFromListeners(widget.listeners),
|
|
3394
3424
|
chat: {
|
|
@@ -3397,6 +3427,7 @@ function mapHypeCup(widget, ctx) {
|
|
|
3397
3427
|
display: {
|
|
3398
3428
|
showWireframes: !!v.showWireframes,
|
|
3399
3429
|
showEventMessages: !!v.textEnabled,
|
|
3430
|
+
showPlatformIcons: false,
|
|
3400
3431
|
messageDuration: duration
|
|
3401
3432
|
},
|
|
3402
3433
|
sprites,
|
|
@@ -4000,7 +4031,7 @@ async function mirrorOneAsset(url, upload, signal, proxyAssetFetch) {
|
|
|
4000
4031
|
const blob = await fetchAssetBlob(url, signal, proxyAssetFetch);
|
|
4001
4032
|
if (signal?.aborted) throw new DOMException("Aborted", "AbortError");
|
|
4002
4033
|
if (blob.size > MAX_MIRROR_ASSET_BYTES) {
|
|
4003
|
-
throw new Error(`Asset is ${(blob.size / (1024 * 1024)).toFixed(1)} MB
|
|
4034
|
+
throw new Error(`Asset is ${(blob.size / (1024 * 1024)).toFixed(1)} MB, over the ${MAX_MIRROR_ASSET_BYTES / (1024 * 1024)} MB import cap.`);
|
|
4004
4035
|
}
|
|
4005
4036
|
const filename = filenameFromURL(url);
|
|
4006
4037
|
const type = blob.type || guessMime(filename);
|
|
@@ -4022,7 +4053,7 @@ async function fetchAssetBlob(url, signal, proxyAssetFetch) {
|
|
|
4022
4053
|
if (!res.ok) throw new Error(`Source returned HTTP ${res.status}`);
|
|
4023
4054
|
const declared = Number(res.headers.get("content-length"));
|
|
4024
4055
|
if (Number.isFinite(declared) && declared > MAX_MIRROR_ASSET_BYTES) {
|
|
4025
|
-
throw new Error(`Asset is ${(declared / (1024 * 1024)).toFixed(1)} MB
|
|
4056
|
+
throw new Error(`Asset is ${(declared / (1024 * 1024)).toFixed(1)} MB, over the ${MAX_MIRROR_ASSET_BYTES / (1024 * 1024)} MB import cap.`);
|
|
4026
4057
|
}
|
|
4027
4058
|
return res.blob();
|
|
4028
4059
|
}
|
|
@@ -4262,7 +4293,7 @@ function decodeJwtPayload(jwt) {
|
|
|
4262
4293
|
}
|
|
4263
4294
|
const claims = parsed;
|
|
4264
4295
|
if (typeof claims.channel !== "string" || typeof claims.authToken !== "string") {
|
|
4265
|
-
throw new SEAuthError("invalid-jwt", "Token payload is missing the 'channel' or 'authToken' claim
|
|
4296
|
+
throw new SEAuthError("invalid-jwt", "Token payload is missing the 'channel' or 'authToken' claim. not a StreamElements account token.");
|
|
4266
4297
|
}
|
|
4267
4298
|
if (typeof claims.exp === "number" && claims.exp * 1e3 < Date.now()) {
|
|
4268
4299
|
throw new SEAuthError("expired", "This StreamElements token has expired. Generate a new one on the SE Account page.");
|
|
@@ -4271,7 +4302,7 @@ function decodeJwtPayload(jwt) {
|
|
|
4271
4302
|
}
|
|
4272
4303
|
var SEClient = class _SEClient {
|
|
4273
4304
|
// `jwt` is held alongside the decoded claims so the client can switch auth
|
|
4274
|
-
// modes without re-parsing. Defaults to `bearer-jwt
|
|
4305
|
+
// modes without re-parsing. Defaults to `bearer-jwt`. confirmed against
|
|
4275
4306
|
// the user's diagnostic 2026-05-16: every endpoint the existing 5 helpers
|
|
4276
4307
|
// hit (users/current, channels/me, overlays, bot/commands, plus
|
|
4277
4308
|
// sessions/loyalty/bits leaderboard transitively) returns 200, AND
|
|
@@ -4359,7 +4390,7 @@ var SEClient = class _SEClient {
|
|
|
4359
4390
|
let detail = "";
|
|
4360
4391
|
try {
|
|
4361
4392
|
const body = await res.json();
|
|
4362
|
-
if (body?.message) detail = `
|
|
4393
|
+
if (body?.message) detail = ` - ${body.message}`;
|
|
4363
4394
|
} catch {
|
|
4364
4395
|
}
|
|
4365
4396
|
throw new Error(`StreamElements API returned HTTP ${res.status}${detail}`);
|
|
@@ -4401,7 +4432,7 @@ async function fetchElementWidgetInstancePublic(channelId, widgetInstanceId, pub
|
|
|
4401
4432
|
let detail = "";
|
|
4402
4433
|
try {
|
|
4403
4434
|
const body = JSON.parse(await result.errorBody());
|
|
4404
|
-
if (body?.message) detail = `
|
|
4435
|
+
if (body?.message) detail = ` - ${body.message}`;
|
|
4405
4436
|
} catch {
|
|
4406
4437
|
}
|
|
4407
4438
|
const reason = result.status === 401 || result.status === 403 ? "StreamElements rejected the widget share token. The share link may have been revoked or made private." : result.status === 404 ? "Element widget not found. Double-check the share URL." : `HTTP ${result.status}`;
|
|
@@ -7343,15 +7374,15 @@ var PLACEHOLDER_WIDGET_TYPES = /* @__PURE__ */ new Set([
|
|
|
7343
7374
|
"streamlabels"
|
|
7344
7375
|
]);
|
|
7345
7376
|
var PLACEHOLDER_REASONS = {
|
|
7346
|
-
donation_ticker: "No native Lumia equivalent yet
|
|
7347
|
-
stream_boss: "Lumia has a native streamboss module
|
|
7348
|
-
credits: "Lumia has a native credits module
|
|
7349
|
-
spin_wheel: "Lumia has a native spinwheel module
|
|
7350
|
-
emote_wall: "Lumia has a native emotebox module
|
|
7351
|
-
sub_train: "No native Lumia equivalent yet
|
|
7377
|
+
donation_ticker: "No native Lumia equivalent yet. imported as a placeholder text layer.",
|
|
7378
|
+
stream_boss: "Lumia has a native streamboss module. set it up directly rather than importing the SL config.",
|
|
7379
|
+
credits: "Lumia has a native credits module. set it up directly rather than importing the SL config.",
|
|
7380
|
+
spin_wheel: "Lumia has a native spinwheel module. set it up directly rather than importing the SL config.",
|
|
7381
|
+
emote_wall: "Lumia has a native emotebox module. set it up directly rather than importing the SL config.",
|
|
7382
|
+
sub_train: "No native Lumia equivalent yet. imported as a placeholder text layer.",
|
|
7352
7383
|
streamlabels: "Use Lumia template variables (e.g. {{twitch_last_follower}}) inside a Text module instead of importing the SL streamlabels config."
|
|
7353
7384
|
};
|
|
7354
|
-
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
|
|
7385
|
+
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. recreate the styling there. The original SL settings are stashed under module.content.importMeta for reference.";
|
|
7355
7386
|
function stampImportMeta(module, widgetType, role, extra = {}) {
|
|
7356
7387
|
stampImportMetaMerge(module, {
|
|
7357
7388
|
source: "streamlabs",
|
|
@@ -7509,7 +7540,7 @@ function importStreamlabsWidget(response, widgetType, options = {}) {
|
|
|
7509
7540
|
});
|
|
7510
7541
|
reviewItems.push({
|
|
7511
7542
|
moduleId: placeholder.module.id,
|
|
7512
|
-
// SL doesn't have a single "widget" object the way SE does
|
|
7543
|
+
// SL doesn't have a single "widget" object the way SE does. we
|
|
7513
7544
|
// stash the settings snapshot here so the review step can show
|
|
7514
7545
|
// the raw config the user imported. Cast through unknown because
|
|
7515
7546
|
// ReviewItem is currently typed against SEWidget; widening the
|
|
@@ -7527,7 +7558,7 @@ function importStreamlabsWidget(response, widgetType, options = {}) {
|
|
|
7527
7558
|
layers.push(placeholder.layer);
|
|
7528
7559
|
modules[placeholder.layer.id] = placeholder.module;
|
|
7529
7560
|
coverage.notes.push(
|
|
7530
|
-
`Unknown Streamlabs widget type "${widgetType}"
|
|
7561
|
+
`Unknown Streamlabs widget type "${widgetType}". imported as a placeholder.`
|
|
7531
7562
|
);
|
|
7532
7563
|
coverage.mappings.push({
|
|
7533
7564
|
seType: `streamlabs:${widgetType}`,
|
|
@@ -7962,7 +7993,7 @@ function StepHeader({
|
|
|
7962
7993
|
] });
|
|
7963
7994
|
}
|
|
7964
7995
|
var JWT_FEATURES = [
|
|
7965
|
-
"All your overlays
|
|
7996
|
+
"All your overlays: pick one, many, or all",
|
|
7966
7997
|
"Live counters (follower / sub / tip / bits goals)",
|
|
7967
7998
|
"Chat bot commands",
|
|
7968
7999
|
"Loyalty points + watch time",
|
|
@@ -8116,7 +8147,7 @@ function StepModePicker({
|
|
|
8116
8147
|
}
|
|
8117
8148
|
)
|
|
8118
8149
|
] }),
|
|
8119
|
-
caveat: "Requires pasting your StreamElements JWT. Stored only in memory
|
|
8150
|
+
caveat: "Requires pasting your StreamElements JWT. Stored only in memory. Never sent to Lumia.",
|
|
8120
8151
|
onClick: onChooseJwt
|
|
8121
8152
|
}
|
|
8122
8153
|
)
|
|
@@ -8141,7 +8172,7 @@ function StepConnect({
|
|
|
8141
8172
|
{
|
|
8142
8173
|
number: 1,
|
|
8143
8174
|
title: "Connect your StreamElements account",
|
|
8144
|
-
subtitle: "Paste your StreamElements JWT to import overlays, live counters, chat commands, loyalty points, watch time, and recent events. The token is read in your browser only
|
|
8175
|
+
subtitle: "Paste your StreamElements JWT to import overlays, live counters, chat commands, loyalty points, watch time, and recent events. The token is read in your browser only. Lumia never sends it to our servers."
|
|
8145
8176
|
}
|
|
8146
8177
|
),
|
|
8147
8178
|
/* @__PURE__ */ jsx14(
|
|
@@ -10790,15 +10821,15 @@ function importElementOverlay(response, options = {}) {
|
|
|
10790
10821
|
// src/se-import/index.ts
|
|
10791
10822
|
var IMPORT_META_KEY2 = "importMeta";
|
|
10792
10823
|
var REVIEW_REASONS = {
|
|
10793
|
-
"se-widget-bit-boss": "No native Lumia equivalent
|
|
10824
|
+
"se-widget-bit-boss": "No native Lumia equivalent. bit boss fight bar with HP/damage states.",
|
|
10794
10825
|
// `se-widget-hype-cup` intentionally omitted — it now routes to the native
|
|
10795
10826
|
// `tipjar` module (see dispatcher.ts) and lands with `partial` status, so it
|
|
10796
10827
|
// never reaches the placeholder-reason fallback. If a future feature flag
|
|
10797
10828
|
// disables tipjar auto-import, add an entry to FLAG_OFF_REASONS instead.
|
|
10798
|
-
"se-widget-media-share": "No native Lumia equivalent
|
|
10799
|
-
"se-widget-contest": "No native Lumia equivalent
|
|
10800
|
-
"se-widget-giveaway": "No native Lumia equivalent
|
|
10801
|
-
"se-widget-botcounter": "No native Lumia equivalent
|
|
10829
|
+
"se-widget-media-share": "No native Lumia equivalent. viewer-submitted media player.",
|
|
10830
|
+
"se-widget-contest": "No native Lumia equivalent. SE-specific contest widget.",
|
|
10831
|
+
"se-widget-giveaway": "No native Lumia equivalent. SE giveaway service.",
|
|
10832
|
+
"se-widget-botcounter": "No native Lumia equivalent. chatter/message counter.",
|
|
10802
10833
|
"se-widget-top-cheerers-list": 'Lumia loyaltyleaderboard needs a "source" extension before it can render cheer rankings.',
|
|
10803
10834
|
"se-widget-top-gifters-list": 'Lumia loyaltyleaderboard needs a "source" extension before it can render gifter rankings.',
|
|
10804
10835
|
"se-widget-top-tippers-list": 'Lumia loyaltyleaderboard needs a "source" extension before it can render tipper rankings.',
|
|
@@ -10808,7 +10839,7 @@ var REVIEW_REASONS = {
|
|
|
10808
10839
|
// so we can't pre-populate the slideshow items. The streamer adds their
|
|
10809
10840
|
// product images by hand in the slideshow editor. Status = 'partial' (see
|
|
10810
10841
|
// dispatcher.ts), so this string surfaces in the review step.
|
|
10811
|
-
"se-widget-merch-products-rotator": "Imported as an empty slideshow. Lumia doesn't fetch your Fourthwall product catalog yet, so add your product images manually in the slideshow editor
|
|
10842
|
+
"se-widget-merch-products-rotator": "Imported as an empty slideshow. Lumia doesn't fetch your Fourthwall product catalog yet, so add your product images manually in the slideshow editor. the layer is pre-positioned and styled to match your SE widget."
|
|
10812
10843
|
// Seasonal widgets (snow / halloween / halloween-2019 / xmas / valentine / easter
|
|
10813
10844
|
// / world-cup) intentionally omitted — they route directly to image/slideshow/
|
|
10814
10845
|
// video modules now (see dispatcher.ts SEASONAL_IMAGE_TYPES), so they always
|
|
@@ -10816,7 +10847,7 @@ var REVIEW_REASONS = {
|
|
|
10816
10847
|
};
|
|
10817
10848
|
function reasonFor(seType, status, flaggedOff) {
|
|
10818
10849
|
if (flaggedOff && FLAG_OFF_REASONS[seType]) return FLAG_OFF_REASONS[seType];
|
|
10819
|
-
return REVIEW_REASONS[seType] ?? (status === "template" ? "Imported as a generic template stub." : "No native mapping yet
|
|
10850
|
+
return REVIEW_REASONS[seType] ?? (status === "template" ? "Imported as a generic template stub." : "No native mapping yet. imported as a labelled text placeholder.");
|
|
10820
10851
|
}
|
|
10821
10852
|
function buildSEBootstrapDescription(overlayId, sourceUrl) {
|
|
10822
10853
|
const head = overlayId ? `Imported from StreamElements overlay ${overlayId}` : "Imported from StreamElements";
|
|
@@ -10846,7 +10877,7 @@ async function fetchSEBootstrap({
|
|
|
10846
10877
|
}) {
|
|
10847
10878
|
if (!apikey) {
|
|
10848
10879
|
throw new Error(
|
|
10849
|
-
"Missing access token. Use the public preview URL (Overlay menu \u2192 Get URL \u2192 Public link)
|
|
10880
|
+
"Missing access token. Use the public preview URL (Overlay menu \u2192 Get URL \u2192 Public link). editor URLs do not include a token."
|
|
10850
10881
|
);
|
|
10851
10882
|
}
|
|
10852
10883
|
const result = await fetchWithProxyFallback(buildBootstrapUrl(overlayId), {
|
|
@@ -10857,7 +10888,7 @@ async function fetchSEBootstrap({
|
|
|
10857
10888
|
let detail = "";
|
|
10858
10889
|
try {
|
|
10859
10890
|
const body = JSON.parse(await result.errorBody());
|
|
10860
|
-
detail = body?.message ?
|
|
10891
|
+
detail = body?.message ? `. ${body.message}` : "";
|
|
10861
10892
|
} catch {
|
|
10862
10893
|
}
|
|
10863
10894
|
const reason = result.status === 401 || result.status === 403 ? "StreamElements rejected the URL token. The preview link may have been revoked." : result.status === 404 ? "Overlay not found. Double-check the URL." : `HTTP ${result.status}`;
|
|
@@ -10999,17 +11030,17 @@ function importSEBootstrap(bootstrap, options = {}) {
|
|
|
10999
11030
|
}
|
|
11000
11031
|
if (coverage.mappings.some((m) => m.status === "placeholder")) {
|
|
11001
11032
|
coverage.notes.push(
|
|
11002
|
-
"Some widgets imported as placeholder text layers
|
|
11033
|
+
"Some widgets imported as placeholder text layers. see SE/05-widget-to-module-mapping.md for status."
|
|
11003
11034
|
);
|
|
11004
11035
|
}
|
|
11005
11036
|
if (coverage.mappings.some((m) => m.seType === "se-widget-merch-goal")) {
|
|
11006
11037
|
coverage.notes.push(
|
|
11007
|
-
"Merch-goal widgets imported as generic goals
|
|
11038
|
+
"Merch-goal widgets imported as generic goals. see SE/03-missing-variables.md \xA78 for the missing data source."
|
|
11008
11039
|
);
|
|
11009
11040
|
}
|
|
11010
11041
|
if (coverage.mappings.some((m) => m.seType === "se-widget-custom-event-list")) {
|
|
11011
11042
|
coverage.notes.push(
|
|
11012
|
-
"\u26A0 Custom widgets are experimental
|
|
11043
|
+
"\u26A0 Custom widgets are experimental. 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."
|
|
11013
11044
|
);
|
|
11014
11045
|
}
|
|
11015
11046
|
const overlay = {
|
|
@@ -11154,7 +11185,7 @@ function buildAIPromptForSEWidget(widget, canvas) {
|
|
|
11154
11185
|
const fullVariables = JSON.stringify(variables, null, 2);
|
|
11155
11186
|
const truncated = fullVariables.length > VARIABLES_BUDGET;
|
|
11156
11187
|
const variablesJson = truncated ? `${fullVariables.slice(0, VARIABLES_BUDGET)}
|
|
11157
|
-
\u2026[truncated
|
|
11188
|
+
\u2026[truncated. ${fullVariables.length - VARIABLES_BUDGET} more bytes elided]` : fullVariables;
|
|
11158
11189
|
return [
|
|
11159
11190
|
"Recreate a StreamElements overlay widget as a Lumia Stream custom overlay (HTML + CSS + JS).",
|
|
11160
11191
|
"",
|
|
@@ -11162,14 +11193,14 @@ function buildAIPromptForSEWidget(widget, canvas) {
|
|
|
11162
11193
|
listener ? `Primary event listener: ${listener}` : "",
|
|
11163
11194
|
listeners ? `Subscribes to events: ${listeners.join(", ")}` : "",
|
|
11164
11195
|
`Canvas: ${landing.width} \xD7 ${landing.height}`,
|
|
11165
|
-
truncated ? "Note: the widget config below is truncated
|
|
11196
|
+
truncated ? "Note: the widget config below is truncated. focus on visible/structural fields and ignore tail content you cannot see." : "",
|
|
11166
11197
|
"",
|
|
11167
11198
|
"Widget configuration (JSON):",
|
|
11168
11199
|
"```json",
|
|
11169
11200
|
variablesJson,
|
|
11170
11201
|
"```",
|
|
11171
11202
|
"",
|
|
11172
|
-
"Constraints
|
|
11203
|
+
"Constraints. your output must run inside a Lumia Custom Overlay sandboxed iframe:",
|
|
11173
11204
|
'- Use `window.Overlay.on("alert", handler)` / `window.Overlay.on("chat", handler)` to receive Lumia events.',
|
|
11174
11205
|
"- Lumia template variables use double-brace syntax: {{username}}, {{amount}}, {{message}}, {{currency}}.",
|
|
11175
11206
|
"- The `window.DATA` object holds user-configurable values (mirrors your `configs`).",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lumiastream/ui",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.3",
|
|
4
4
|
"author": "Lumia Stream",
|
|
5
5
|
"license": "ISC",
|
|
6
6
|
"description": "Lumia UI Kit",
|
|
@@ -128,7 +128,7 @@
|
|
|
128
128
|
"vitest": "^4.1.6"
|
|
129
129
|
},
|
|
130
130
|
"dependencies": {
|
|
131
|
-
"@lumiastream/lumia-translations": "1.16.
|
|
131
|
+
"@lumiastream/lumia-translations": "1.16.1",
|
|
132
132
|
"@lumiastream/lumia-types": "^3.3.7-alpha.2",
|
|
133
133
|
"classnames": "^2.5.1",
|
|
134
134
|
"globals": "^17.4.0",
|