@lumiastream/ui 0.6.0 → 0.6.2

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 CHANGED
@@ -4865,6 +4865,65 @@ var RESERVED_FIELD_KEYS = /* @__PURE__ */ new Set([
4865
4865
  "widgetAuthor",
4866
4866
  "widgetDuration"
4867
4867
  ]);
4868
+ var SE_LISTENER_TO_LUMIA_EVENTS = {
4869
+ message: ["chat"],
4870
+ "tip-latest": ["alert", "alert.trigger"],
4871
+ "merch-latest": ["alert", "alert.trigger"],
4872
+ "subscriber-latest": ["alert", "alert.trigger"],
4873
+ "subscriber-resub-latest": ["alert", "alert.trigger"],
4874
+ "subscriber-gifted-latest": ["alert", "alert.trigger"],
4875
+ "subscriber-bomb-latest": ["alert", "alert.trigger"],
4876
+ "follower-latest": ["alert", "alert.trigger"],
4877
+ "cheer-latest": ["alert", "alert.trigger"],
4878
+ "raid-latest": ["alert", "alert.trigger"],
4879
+ "host-latest": ["alert", "alert.trigger"],
4880
+ "redemption-latest": ["alert", "alert.trigger"],
4881
+ "hypetrain-start": ["alert", "alert.trigger"],
4882
+ "hypetrain-level-progress": ["alert", "alert.trigger"],
4883
+ "hypetrain-end": ["alert", "alert.trigger"],
4884
+ "sponsor-latest": ["alert", "alert.trigger"],
4885
+ "member-latest": ["alert", "alert.trigger"],
4886
+ "superchat-latest": ["alert", "alert.trigger"],
4887
+ "kappagen-launched": ["alert", "alert.trigger"]
4888
+ };
4889
+ function listenersToLumiaEvents(listeners, listener) {
4890
+ const keys = /* @__PURE__ */ new Set();
4891
+ if (Array.isArray(listeners)) {
4892
+ for (const k of listeners) if (typeof k === "string") keys.add(k);
4893
+ } else if (listeners && typeof listeners === "object") {
4894
+ for (const [k, v] of Object.entries(listeners)) if (v) keys.add(k);
4895
+ }
4896
+ if (typeof listener === "string" && listener) keys.add(listener);
4897
+ const out = /* @__PURE__ */ new Set();
4898
+ for (const k of keys) {
4899
+ const mapped = SE_LISTENER_TO_LUMIA_EVENTS[k];
4900
+ if (mapped) for (const e of mapped) out.add(e);
4901
+ }
4902
+ return [...out];
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
+ }
4868
4927
  function translateConfigs(sourceFields) {
4869
4928
  if (!sourceFields) return [];
4870
4929
  return Object.entries(sourceFields).filter(([key]) => !RESERVED_FIELD_KEYS.has(key)).map(([key, f]) => {
@@ -4913,6 +4972,19 @@ function mapCustom(widget, ctx) {
4913
4972
  const fieldData = parse(v.fieldData) ?? v.fieldData;
4914
4973
  const widgetProvider = widget.provider;
4915
4974
  const provider = widgetProvider === "kick" || widgetProvider === "youtube" || widgetProvider === "twitch" ? widgetProvider : ctx?.provider ?? "twitch";
4975
+ const eventsList = listenersToLumiaEvents(
4976
+ widget.listeners,
4977
+ widget.listener
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");
4986
+ const data = { ...fieldData ?? {} };
4987
+ if (eventsList.length > 0) data.events = eventsList;
4916
4988
  return buildUnit(
4917
4989
  widget,
4918
4990
  "custom",
@@ -4928,7 +5000,7 @@ function mapCustom(widget, ctx) {
4928
5000
  css: v.css ?? "",
4929
5001
  js: v.js ?? "",
4930
5002
  configs: translateConfigs(fields),
4931
- data: fieldData ?? {},
5003
+ data,
4932
5004
  flavor: "streamelements",
4933
5005
  // Native field on the Custom module content. The compatibility
4934
5006
  // shim in Embed.tsx reads this to pick the right listener-name
@@ -7038,7 +7110,7 @@ async function mirrorOneAsset(url, upload, signal, proxyAssetFetch) {
7038
7110
  const blob = await fetchAssetBlob(url, signal, proxyAssetFetch);
7039
7111
  if (signal?.aborted) throw new DOMException("Aborted", "AbortError");
7040
7112
  if (blob.size > MAX_MIRROR_ASSET_BYTES) {
7041
- throw new Error(`Asset is ${(blob.size / (1024 * 1024)).toFixed(1)} MB \u2014 over the ${MAX_MIRROR_ASSET_BYTES / (1024 * 1024)} MB import cap.`);
7113
+ throw new Error(`Asset is ${(blob.size / (1024 * 1024)).toFixed(1)} MB, over the ${MAX_MIRROR_ASSET_BYTES / (1024 * 1024)} MB import cap.`);
7042
7114
  }
7043
7115
  const filename = filenameFromURL(url);
7044
7116
  const type = blob.type || guessMime(filename);
@@ -7060,7 +7132,7 @@ async function fetchAssetBlob(url, signal, proxyAssetFetch) {
7060
7132
  if (!res.ok) throw new Error(`Source returned HTTP ${res.status}`);
7061
7133
  const declared = Number(res.headers.get("content-length"));
7062
7134
  if (Number.isFinite(declared) && declared > MAX_MIRROR_ASSET_BYTES) {
7063
- throw new Error(`Asset is ${(declared / (1024 * 1024)).toFixed(1)} MB \u2014 over the ${MAX_MIRROR_ASSET_BYTES / (1024 * 1024)} MB import cap.`);
7135
+ throw new Error(`Asset is ${(declared / (1024 * 1024)).toFixed(1)} MB, over the ${MAX_MIRROR_ASSET_BYTES / (1024 * 1024)} MB import cap.`);
7064
7136
  }
7065
7137
  return res.blob();
7066
7138
  }
@@ -7300,7 +7372,7 @@ function decodeJwtPayload(jwt) {
7300
7372
  }
7301
7373
  const claims = parsed;
7302
7374
  if (typeof claims.channel !== "string" || typeof claims.authToken !== "string") {
7303
- throw new SEAuthError("invalid-jwt", "Token payload is missing the 'channel' or 'authToken' claim \u2014 not a StreamElements account token.");
7375
+ throw new SEAuthError("invalid-jwt", "Token payload is missing the 'channel' or 'authToken' claim. not a StreamElements account token.");
7304
7376
  }
7305
7377
  if (typeof claims.exp === "number" && claims.exp * 1e3 < Date.now()) {
7306
7378
  throw new SEAuthError("expired", "This StreamElements token has expired. Generate a new one on the SE Account page.");
@@ -7309,7 +7381,7 @@ function decodeJwtPayload(jwt) {
7309
7381
  }
7310
7382
  var SEClient = class _SEClient {
7311
7383
  // `jwt` is held alongside the decoded claims so the client can switch auth
7312
- // modes without re-parsing. Defaults to `bearer-jwt` confirmed against
7384
+ // modes without re-parsing. Defaults to `bearer-jwt`. confirmed against
7313
7385
  // the user's diagnostic 2026-05-16: every endpoint the existing 5 helpers
7314
7386
  // hit (users/current, channels/me, overlays, bot/commands, plus
7315
7387
  // sessions/loyalty/bits leaderboard transitively) returns 200, AND
@@ -7397,7 +7469,7 @@ var SEClient = class _SEClient {
7397
7469
  let detail = "";
7398
7470
  try {
7399
7471
  const body = await res.json();
7400
- if (body?.message) detail = ` \u2014 ${body.message}`;
7472
+ if (body?.message) detail = ` - ${body.message}`;
7401
7473
  } catch {
7402
7474
  }
7403
7475
  throw new Error(`StreamElements API returned HTTP ${res.status}${detail}`);
@@ -7439,7 +7511,7 @@ async function fetchElementWidgetInstancePublic(channelId, widgetInstanceId, pub
7439
7511
  let detail = "";
7440
7512
  try {
7441
7513
  const body = JSON.parse(await result.errorBody());
7442
- if (body?.message) detail = ` \u2014 ${body.message}`;
7514
+ if (body?.message) detail = ` - ${body.message}`;
7443
7515
  } catch {
7444
7516
  }
7445
7517
  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}`;
@@ -8447,15 +8519,15 @@ var PLACEHOLDER_WIDGET_TYPES = /* @__PURE__ */ new Set([
8447
8519
  "streamlabels"
8448
8520
  ]);
8449
8521
  var PLACEHOLDER_REASONS = {
8450
- donation_ticker: "No native Lumia equivalent yet \u2014 imported as a placeholder text layer.",
8451
- stream_boss: "Lumia has a native streamboss module \u2014 set it up directly rather than importing the SL config.",
8452
- credits: "Lumia has a native credits module \u2014 set it up directly rather than importing the SL config.",
8453
- spin_wheel: "Lumia has a native spinwheel module \u2014 set it up directly rather than importing the SL config.",
8454
- emote_wall: "Lumia has a native emotebox module \u2014 set it up directly rather than importing the SL config.",
8455
- sub_train: "No native Lumia equivalent yet \u2014 imported as a placeholder text layer.",
8522
+ donation_ticker: "No native Lumia equivalent yet. imported as a placeholder text layer.",
8523
+ stream_boss: "Lumia has a native streamboss module. set it up directly rather than importing the SL config.",
8524
+ credits: "Lumia has a native credits module. set it up directly rather than importing the SL config.",
8525
+ spin_wheel: "Lumia has a native spinwheel module. set it up directly rather than importing the SL config.",
8526
+ emote_wall: "Lumia has a native emotebox module. set it up directly rather than importing the SL config.",
8527
+ sub_train: "No native Lumia equivalent yet. imported as a placeholder text layer.",
8456
8528
  streamlabels: "Use Lumia template variables (e.g. {{twitch_last_follower}}) inside a Text module instead of importing the SL streamlabels config."
8457
8529
  };
8458
- 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.";
8530
+ 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.";
8459
8531
  function stampImportMeta(module, widgetType, role, extra = {}) {
8460
8532
  stampImportMetaMerge(module, {
8461
8533
  source: "streamlabs",
@@ -8613,7 +8685,7 @@ function importStreamlabsWidget(response, widgetType, options = {}) {
8613
8685
  });
8614
8686
  reviewItems.push({
8615
8687
  moduleId: placeholder.module.id,
8616
- // SL doesn't have a single "widget" object the way SE does we
8688
+ // SL doesn't have a single "widget" object the way SE does. we
8617
8689
  // stash the settings snapshot here so the review step can show
8618
8690
  // the raw config the user imported. Cast through unknown because
8619
8691
  // ReviewItem is currently typed against SEWidget; widening the
@@ -8631,7 +8703,7 @@ function importStreamlabsWidget(response, widgetType, options = {}) {
8631
8703
  layers.push(placeholder.layer);
8632
8704
  modules[placeholder.layer.id] = placeholder.module;
8633
8705
  coverage.notes.push(
8634
- `Unknown Streamlabs widget type "${widgetType}" \u2014 imported as a placeholder.`
8706
+ `Unknown Streamlabs widget type "${widgetType}". imported as a placeholder.`
8635
8707
  );
8636
8708
  coverage.mappings.push({
8637
8709
  seType: `streamlabs:${widgetType}`,
@@ -9044,7 +9116,7 @@ function StepHeader({
9044
9116
  ] });
9045
9117
  }
9046
9118
  var JWT_FEATURES = [
9047
- "All your overlays \u2014 pick one, many, or all",
9119
+ "All your overlays: pick one, many, or all",
9048
9120
  "Live counters (follower / sub / tip / bits goals)",
9049
9121
  "Chat bot commands",
9050
9122
  "Loyalty points + watch time",
@@ -9198,7 +9270,7 @@ function StepModePicker({
9198
9270
  }
9199
9271
  )
9200
9272
  ] }),
9201
- caveat: "Requires pasting your StreamElements JWT. Stored only in memory \u2014 never sent to Lumia.",
9273
+ caveat: "Requires pasting your StreamElements JWT. Stored only in memory. Never sent to Lumia.",
9202
9274
  onClick: onChooseJwt
9203
9275
  }
9204
9276
  )
@@ -9223,7 +9295,7 @@ function StepConnect({
9223
9295
  {
9224
9296
  number: 1,
9225
9297
  title: "Connect your StreamElements account",
9226
- 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 \u2014 Lumia never sends it to our servers."
9298
+ 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."
9227
9299
  }
9228
9300
  ),
9229
9301
  /* @__PURE__ */ jsx14(
@@ -11872,15 +11944,15 @@ function importElementOverlay(response, options = {}) {
11872
11944
  // src/se-import/index.ts
11873
11945
  var IMPORT_META_KEY2 = "importMeta";
11874
11946
  var REVIEW_REASONS = {
11875
- "se-widget-bit-boss": "No native Lumia equivalent \u2014 bit boss fight bar with HP/damage states.",
11947
+ "se-widget-bit-boss": "No native Lumia equivalent. bit boss fight bar with HP/damage states.",
11876
11948
  // `se-widget-hype-cup` intentionally omitted — it now routes to the native
11877
11949
  // `tipjar` module (see dispatcher.ts) and lands with `partial` status, so it
11878
11950
  // never reaches the placeholder-reason fallback. If a future feature flag
11879
11951
  // disables tipjar auto-import, add an entry to FLAG_OFF_REASONS instead.
11880
- "se-widget-media-share": "No native Lumia equivalent \u2014 viewer-submitted media player.",
11881
- "se-widget-contest": "No native Lumia equivalent \u2014 SE-specific contest widget.",
11882
- "se-widget-giveaway": "No native Lumia equivalent \u2014 SE giveaway service.",
11883
- "se-widget-botcounter": "No native Lumia equivalent \u2014 chatter/message counter.",
11952
+ "se-widget-media-share": "No native Lumia equivalent. viewer-submitted media player.",
11953
+ "se-widget-contest": "No native Lumia equivalent. SE-specific contest widget.",
11954
+ "se-widget-giveaway": "No native Lumia equivalent. SE giveaway service.",
11955
+ "se-widget-botcounter": "No native Lumia equivalent. chatter/message counter.",
11884
11956
  "se-widget-top-cheerers-list": 'Lumia loyaltyleaderboard needs a "source" extension before it can render cheer rankings.',
11885
11957
  "se-widget-top-gifters-list": 'Lumia loyaltyleaderboard needs a "source" extension before it can render gifter rankings.',
11886
11958
  "se-widget-top-tippers-list": 'Lumia loyaltyleaderboard needs a "source" extension before it can render tipper rankings.',
@@ -11890,7 +11962,7 @@ var REVIEW_REASONS = {
11890
11962
  // so we can't pre-populate the slideshow items. The streamer adds their
11891
11963
  // product images by hand in the slideshow editor. Status = 'partial' (see
11892
11964
  // dispatcher.ts), so this string surfaces in the review step.
11893
- "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 \u2014 the layer is pre-positioned and styled to match your SE widget."
11965
+ "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."
11894
11966
  // Seasonal widgets (snow / halloween / halloween-2019 / xmas / valentine / easter
11895
11967
  // / world-cup) intentionally omitted — they route directly to image/slideshow/
11896
11968
  // video modules now (see dispatcher.ts SEASONAL_IMAGE_TYPES), so they always
@@ -11898,7 +11970,7 @@ var REVIEW_REASONS = {
11898
11970
  };
11899
11971
  function reasonFor(seType, status, flaggedOff) {
11900
11972
  if (flaggedOff && FLAG_OFF_REASONS[seType]) return FLAG_OFF_REASONS[seType];
11901
- return REVIEW_REASONS[seType] ?? (status === "template" ? "Imported as a generic template stub." : "No native mapping yet \u2014 imported as a labelled text placeholder.");
11973
+ return REVIEW_REASONS[seType] ?? (status === "template" ? "Imported as a generic template stub." : "No native mapping yet. imported as a labelled text placeholder.");
11902
11974
  }
11903
11975
  function buildSEBootstrapDescription(overlayId, sourceUrl) {
11904
11976
  const head = overlayId ? `Imported from StreamElements overlay ${overlayId}` : "Imported from StreamElements";
@@ -11928,7 +12000,7 @@ async function fetchSEBootstrap({
11928
12000
  }) {
11929
12001
  if (!apikey) {
11930
12002
  throw new Error(
11931
- "Missing access token. Use the public preview URL (Overlay menu \u2192 Get URL \u2192 Public link) \u2014 editor URLs do not include a token."
12003
+ "Missing access token. Use the public preview URL (Overlay menu \u2192 Get URL \u2192 Public link). editor URLs do not include a token."
11932
12004
  );
11933
12005
  }
11934
12006
  const result = await fetchWithProxyFallback(buildBootstrapUrl(overlayId), {
@@ -11939,7 +12011,7 @@ async function fetchSEBootstrap({
11939
12011
  let detail = "";
11940
12012
  try {
11941
12013
  const body = JSON.parse(await result.errorBody());
11942
- detail = body?.message ? ` \u2014 ${body.message}` : "";
12014
+ detail = body?.message ? `. ${body.message}` : "";
11943
12015
  } catch {
11944
12016
  }
11945
12017
  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}`;
@@ -12081,17 +12153,17 @@ function importSEBootstrap(bootstrap, options = {}) {
12081
12153
  }
12082
12154
  if (coverage.mappings.some((m) => m.status === "placeholder")) {
12083
12155
  coverage.notes.push(
12084
- "Some widgets imported as placeholder text layers \u2014 see SE/05-widget-to-module-mapping.md for status."
12156
+ "Some widgets imported as placeholder text layers. see SE/05-widget-to-module-mapping.md for status."
12085
12157
  );
12086
12158
  }
12087
12159
  if (coverage.mappings.some((m) => m.seType === "se-widget-merch-goal")) {
12088
12160
  coverage.notes.push(
12089
- "Merch-goal widgets imported as generic goals \u2014 see SE/03-missing-variables.md \xA78 for the missing data source."
12161
+ "Merch-goal widgets imported as generic goals. see SE/03-missing-variables.md \xA78 for the missing data source."
12090
12162
  );
12091
12163
  }
12092
12164
  if (coverage.mappings.some((m) => m.seType === "se-widget-custom-event-list")) {
12093
12165
  coverage.notes.push(
12094
- "\u26A0 Custom widgets are experimental \u2014 they run under the SE compatibility shim (Embed.tsx) which handles most SE APIs but may not cover every edge case. Verify each custom layer renders correctly in the editor."
12166
+ "\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."
12095
12167
  );
12096
12168
  }
12097
12169
  const overlay = {
@@ -12236,7 +12308,7 @@ function buildAIPromptForSEWidget(widget, canvas) {
12236
12308
  const fullVariables = JSON.stringify(variables, null, 2);
12237
12309
  const truncated = fullVariables.length > VARIABLES_BUDGET;
12238
12310
  const variablesJson = truncated ? `${fullVariables.slice(0, VARIABLES_BUDGET)}
12239
- \u2026[truncated \u2014 ${fullVariables.length - VARIABLES_BUDGET} more bytes elided]` : fullVariables;
12311
+ \u2026[truncated. ${fullVariables.length - VARIABLES_BUDGET} more bytes elided]` : fullVariables;
12240
12312
  return [
12241
12313
  "Recreate a StreamElements overlay widget as a Lumia Stream custom overlay (HTML + CSS + JS).",
12242
12314
  "",
@@ -12244,14 +12316,14 @@ function buildAIPromptForSEWidget(widget, canvas) {
12244
12316
  listener ? `Primary event listener: ${listener}` : "",
12245
12317
  listeners ? `Subscribes to events: ${listeners.join(", ")}` : "",
12246
12318
  `Canvas: ${landing.width} \xD7 ${landing.height}`,
12247
- truncated ? "Note: the widget config below is truncated \u2014 focus on visible/structural fields and ignore tail content you cannot see." : "",
12319
+ truncated ? "Note: the widget config below is truncated. focus on visible/structural fields and ignore tail content you cannot see." : "",
12248
12320
  "",
12249
12321
  "Widget configuration (JSON):",
12250
12322
  "```json",
12251
12323
  variablesJson,
12252
12324
  "```",
12253
12325
  "",
12254
- "Constraints \u2014 your output must run inside a Lumia Custom Overlay sandboxed iframe:",
12326
+ "Constraints. your output must run inside a Lumia Custom Overlay sandboxed iframe:",
12255
12327
  '- Use `window.Overlay.on("alert", handler)` / `window.Overlay.on("chat", handler)` to receive Lumia events.',
12256
12328
  "- Lumia template variables use double-brace syntax: {{username}}, {{amount}}, {{message}}, {{currency}}.",
12257
12329
  "- The `window.DATA` object holds user-configurable values (mirrors your `configs`).",
@@ -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 see
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 the
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
@@ -1785,6 +1785,65 @@ var RESERVED_FIELD_KEYS = /* @__PURE__ */ new Set([
1785
1785
  "widgetAuthor",
1786
1786
  "widgetDuration"
1787
1787
  ]);
1788
+ var SE_LISTENER_TO_LUMIA_EVENTS = {
1789
+ message: ["chat"],
1790
+ "tip-latest": ["alert", "alert.trigger"],
1791
+ "merch-latest": ["alert", "alert.trigger"],
1792
+ "subscriber-latest": ["alert", "alert.trigger"],
1793
+ "subscriber-resub-latest": ["alert", "alert.trigger"],
1794
+ "subscriber-gifted-latest": ["alert", "alert.trigger"],
1795
+ "subscriber-bomb-latest": ["alert", "alert.trigger"],
1796
+ "follower-latest": ["alert", "alert.trigger"],
1797
+ "cheer-latest": ["alert", "alert.trigger"],
1798
+ "raid-latest": ["alert", "alert.trigger"],
1799
+ "host-latest": ["alert", "alert.trigger"],
1800
+ "redemption-latest": ["alert", "alert.trigger"],
1801
+ "hypetrain-start": ["alert", "alert.trigger"],
1802
+ "hypetrain-level-progress": ["alert", "alert.trigger"],
1803
+ "hypetrain-end": ["alert", "alert.trigger"],
1804
+ "sponsor-latest": ["alert", "alert.trigger"],
1805
+ "member-latest": ["alert", "alert.trigger"],
1806
+ "superchat-latest": ["alert", "alert.trigger"],
1807
+ "kappagen-launched": ["alert", "alert.trigger"]
1808
+ };
1809
+ function listenersToLumiaEvents(listeners, listener) {
1810
+ const keys = /* @__PURE__ */ new Set();
1811
+ if (Array.isArray(listeners)) {
1812
+ for (const k of listeners) if (typeof k === "string") keys.add(k);
1813
+ } else if (listeners && typeof listeners === "object") {
1814
+ for (const [k, v] of Object.entries(listeners)) if (v) keys.add(k);
1815
+ }
1816
+ if (typeof listener === "string" && listener) keys.add(listener);
1817
+ const out = /* @__PURE__ */ new Set();
1818
+ for (const k of keys) {
1819
+ const mapped = SE_LISTENER_TO_LUMIA_EVENTS[k];
1820
+ if (mapped) for (const e of mapped) out.add(e);
1821
+ }
1822
+ return [...out];
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
+ }
1788
1847
  function translateConfigs(sourceFields) {
1789
1848
  if (!sourceFields) return [];
1790
1849
  return Object.entries(sourceFields).filter(([key]) => !RESERVED_FIELD_KEYS.has(key)).map(([key, f]) => {
@@ -1833,6 +1892,19 @@ function mapCustom(widget, ctx) {
1833
1892
  const fieldData = parse(v.fieldData) ?? v.fieldData;
1834
1893
  const widgetProvider = widget.provider;
1835
1894
  const provider = widgetProvider === "kick" || widgetProvider === "youtube" || widgetProvider === "twitch" ? widgetProvider : ctx?.provider ?? "twitch";
1895
+ const eventsList = listenersToLumiaEvents(
1896
+ widget.listeners,
1897
+ widget.listener
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");
1906
+ const data = { ...fieldData ?? {} };
1907
+ if (eventsList.length > 0) data.events = eventsList;
1836
1908
  return buildUnit(
1837
1909
  widget,
1838
1910
  "custom",
@@ -1848,7 +1920,7 @@ function mapCustom(widget, ctx) {
1848
1920
  css: v.css ?? "",
1849
1921
  js: v.js ?? "",
1850
1922
  configs: translateConfigs(fields),
1851
- data: fieldData ?? {},
1923
+ data,
1852
1924
  flavor: "streamelements",
1853
1925
  // Native field on the Custom module content. The compatibility
1854
1926
  // shim in Embed.tsx reads this to pick the right listener-name
@@ -3958,7 +4030,7 @@ async function mirrorOneAsset(url, upload, signal, proxyAssetFetch) {
3958
4030
  const blob = await fetchAssetBlob(url, signal, proxyAssetFetch);
3959
4031
  if (signal?.aborted) throw new DOMException("Aborted", "AbortError");
3960
4032
  if (blob.size > MAX_MIRROR_ASSET_BYTES) {
3961
- throw new Error(`Asset is ${(blob.size / (1024 * 1024)).toFixed(1)} MB \u2014 over the ${MAX_MIRROR_ASSET_BYTES / (1024 * 1024)} MB import cap.`);
4033
+ throw new Error(`Asset is ${(blob.size / (1024 * 1024)).toFixed(1)} MB, over the ${MAX_MIRROR_ASSET_BYTES / (1024 * 1024)} MB import cap.`);
3962
4034
  }
3963
4035
  const filename = filenameFromURL(url);
3964
4036
  const type = blob.type || guessMime(filename);
@@ -3980,7 +4052,7 @@ async function fetchAssetBlob(url, signal, proxyAssetFetch) {
3980
4052
  if (!res.ok) throw new Error(`Source returned HTTP ${res.status}`);
3981
4053
  const declared = Number(res.headers.get("content-length"));
3982
4054
  if (Number.isFinite(declared) && declared > MAX_MIRROR_ASSET_BYTES) {
3983
- throw new Error(`Asset is ${(declared / (1024 * 1024)).toFixed(1)} MB \u2014 over the ${MAX_MIRROR_ASSET_BYTES / (1024 * 1024)} MB import cap.`);
4055
+ throw new Error(`Asset is ${(declared / (1024 * 1024)).toFixed(1)} MB, over the ${MAX_MIRROR_ASSET_BYTES / (1024 * 1024)} MB import cap.`);
3984
4056
  }
3985
4057
  return res.blob();
3986
4058
  }
@@ -4220,7 +4292,7 @@ function decodeJwtPayload(jwt) {
4220
4292
  }
4221
4293
  const claims = parsed;
4222
4294
  if (typeof claims.channel !== "string" || typeof claims.authToken !== "string") {
4223
- throw new SEAuthError("invalid-jwt", "Token payload is missing the 'channel' or 'authToken' claim \u2014 not a StreamElements account token.");
4295
+ throw new SEAuthError("invalid-jwt", "Token payload is missing the 'channel' or 'authToken' claim. not a StreamElements account token.");
4224
4296
  }
4225
4297
  if (typeof claims.exp === "number" && claims.exp * 1e3 < Date.now()) {
4226
4298
  throw new SEAuthError("expired", "This StreamElements token has expired. Generate a new one on the SE Account page.");
@@ -4229,7 +4301,7 @@ function decodeJwtPayload(jwt) {
4229
4301
  }
4230
4302
  var SEClient = class _SEClient {
4231
4303
  // `jwt` is held alongside the decoded claims so the client can switch auth
4232
- // modes without re-parsing. Defaults to `bearer-jwt` confirmed against
4304
+ // modes without re-parsing. Defaults to `bearer-jwt`. confirmed against
4233
4305
  // the user's diagnostic 2026-05-16: every endpoint the existing 5 helpers
4234
4306
  // hit (users/current, channels/me, overlays, bot/commands, plus
4235
4307
  // sessions/loyalty/bits leaderboard transitively) returns 200, AND
@@ -4317,7 +4389,7 @@ var SEClient = class _SEClient {
4317
4389
  let detail = "";
4318
4390
  try {
4319
4391
  const body = await res.json();
4320
- if (body?.message) detail = ` \u2014 ${body.message}`;
4392
+ if (body?.message) detail = ` - ${body.message}`;
4321
4393
  } catch {
4322
4394
  }
4323
4395
  throw new Error(`StreamElements API returned HTTP ${res.status}${detail}`);
@@ -4359,7 +4431,7 @@ async function fetchElementWidgetInstancePublic(channelId, widgetInstanceId, pub
4359
4431
  let detail = "";
4360
4432
  try {
4361
4433
  const body = JSON.parse(await result.errorBody());
4362
- if (body?.message) detail = ` \u2014 ${body.message}`;
4434
+ if (body?.message) detail = ` - ${body.message}`;
4363
4435
  } catch {
4364
4436
  }
4365
4437
  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}`;
@@ -7301,15 +7373,15 @@ var PLACEHOLDER_WIDGET_TYPES = /* @__PURE__ */ new Set([
7301
7373
  "streamlabels"
7302
7374
  ]);
7303
7375
  var PLACEHOLDER_REASONS = {
7304
- donation_ticker: "No native Lumia equivalent yet \u2014 imported as a placeholder text layer.",
7305
- stream_boss: "Lumia has a native streamboss module \u2014 set it up directly rather than importing the SL config.",
7306
- credits: "Lumia has a native credits module \u2014 set it up directly rather than importing the SL config.",
7307
- spin_wheel: "Lumia has a native spinwheel module \u2014 set it up directly rather than importing the SL config.",
7308
- emote_wall: "Lumia has a native emotebox module \u2014 set it up directly rather than importing the SL config.",
7309
- sub_train: "No native Lumia equivalent yet \u2014 imported as a placeholder text layer.",
7376
+ donation_ticker: "No native Lumia equivalent yet. imported as a placeholder text layer.",
7377
+ stream_boss: "Lumia has a native streamboss module. set it up directly rather than importing the SL config.",
7378
+ credits: "Lumia has a native credits module. set it up directly rather than importing the SL config.",
7379
+ spin_wheel: "Lumia has a native spinwheel module. set it up directly rather than importing the SL config.",
7380
+ emote_wall: "Lumia has a native emotebox module. set it up directly rather than importing the SL config.",
7381
+ sub_train: "No native Lumia equivalent yet. imported as a placeholder text layer.",
7310
7382
  streamlabels: "Use Lumia template variables (e.g. {{twitch_last_follower}}) inside a Text module instead of importing the SL streamlabels config."
7311
7383
  };
7312
- 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.";
7384
+ 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.";
7313
7385
  function stampImportMeta(module, widgetType, role, extra = {}) {
7314
7386
  stampImportMetaMerge(module, {
7315
7387
  source: "streamlabs",
@@ -7467,7 +7539,7 @@ function importStreamlabsWidget(response, widgetType, options = {}) {
7467
7539
  });
7468
7540
  reviewItems.push({
7469
7541
  moduleId: placeholder.module.id,
7470
- // SL doesn't have a single "widget" object the way SE does we
7542
+ // SL doesn't have a single "widget" object the way SE does. we
7471
7543
  // stash the settings snapshot here so the review step can show
7472
7544
  // the raw config the user imported. Cast through unknown because
7473
7545
  // ReviewItem is currently typed against SEWidget; widening the
@@ -7485,7 +7557,7 @@ function importStreamlabsWidget(response, widgetType, options = {}) {
7485
7557
  layers.push(placeholder.layer);
7486
7558
  modules[placeholder.layer.id] = placeholder.module;
7487
7559
  coverage.notes.push(
7488
- `Unknown Streamlabs widget type "${widgetType}" \u2014 imported as a placeholder.`
7560
+ `Unknown Streamlabs widget type "${widgetType}". imported as a placeholder.`
7489
7561
  );
7490
7562
  coverage.mappings.push({
7491
7563
  seType: `streamlabs:${widgetType}`,
@@ -7920,7 +7992,7 @@ function StepHeader({
7920
7992
  ] });
7921
7993
  }
7922
7994
  var JWT_FEATURES = [
7923
- "All your overlays \u2014 pick one, many, or all",
7995
+ "All your overlays: pick one, many, or all",
7924
7996
  "Live counters (follower / sub / tip / bits goals)",
7925
7997
  "Chat bot commands",
7926
7998
  "Loyalty points + watch time",
@@ -8074,7 +8146,7 @@ function StepModePicker({
8074
8146
  }
8075
8147
  )
8076
8148
  ] }),
8077
- caveat: "Requires pasting your StreamElements JWT. Stored only in memory \u2014 never sent to Lumia.",
8149
+ caveat: "Requires pasting your StreamElements JWT. Stored only in memory. Never sent to Lumia.",
8078
8150
  onClick: onChooseJwt
8079
8151
  }
8080
8152
  )
@@ -8099,7 +8171,7 @@ function StepConnect({
8099
8171
  {
8100
8172
  number: 1,
8101
8173
  title: "Connect your StreamElements account",
8102
- 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 \u2014 Lumia never sends it to our servers."
8174
+ 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."
8103
8175
  }
8104
8176
  ),
8105
8177
  /* @__PURE__ */ jsx14(
@@ -10748,15 +10820,15 @@ function importElementOverlay(response, options = {}) {
10748
10820
  // src/se-import/index.ts
10749
10821
  var IMPORT_META_KEY2 = "importMeta";
10750
10822
  var REVIEW_REASONS = {
10751
- "se-widget-bit-boss": "No native Lumia equivalent \u2014 bit boss fight bar with HP/damage states.",
10823
+ "se-widget-bit-boss": "No native Lumia equivalent. bit boss fight bar with HP/damage states.",
10752
10824
  // `se-widget-hype-cup` intentionally omitted — it now routes to the native
10753
10825
  // `tipjar` module (see dispatcher.ts) and lands with `partial` status, so it
10754
10826
  // never reaches the placeholder-reason fallback. If a future feature flag
10755
10827
  // disables tipjar auto-import, add an entry to FLAG_OFF_REASONS instead.
10756
- "se-widget-media-share": "No native Lumia equivalent \u2014 viewer-submitted media player.",
10757
- "se-widget-contest": "No native Lumia equivalent \u2014 SE-specific contest widget.",
10758
- "se-widget-giveaway": "No native Lumia equivalent \u2014 SE giveaway service.",
10759
- "se-widget-botcounter": "No native Lumia equivalent \u2014 chatter/message counter.",
10828
+ "se-widget-media-share": "No native Lumia equivalent. viewer-submitted media player.",
10829
+ "se-widget-contest": "No native Lumia equivalent. SE-specific contest widget.",
10830
+ "se-widget-giveaway": "No native Lumia equivalent. SE giveaway service.",
10831
+ "se-widget-botcounter": "No native Lumia equivalent. chatter/message counter.",
10760
10832
  "se-widget-top-cheerers-list": 'Lumia loyaltyleaderboard needs a "source" extension before it can render cheer rankings.',
10761
10833
  "se-widget-top-gifters-list": 'Lumia loyaltyleaderboard needs a "source" extension before it can render gifter rankings.',
10762
10834
  "se-widget-top-tippers-list": 'Lumia loyaltyleaderboard needs a "source" extension before it can render tipper rankings.',
@@ -10766,7 +10838,7 @@ var REVIEW_REASONS = {
10766
10838
  // so we can't pre-populate the slideshow items. The streamer adds their
10767
10839
  // product images by hand in the slideshow editor. Status = 'partial' (see
10768
10840
  // dispatcher.ts), so this string surfaces in the review step.
10769
- "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 \u2014 the layer is pre-positioned and styled to match your SE widget."
10841
+ "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."
10770
10842
  // Seasonal widgets (snow / halloween / halloween-2019 / xmas / valentine / easter
10771
10843
  // / world-cup) intentionally omitted — they route directly to image/slideshow/
10772
10844
  // video modules now (see dispatcher.ts SEASONAL_IMAGE_TYPES), so they always
@@ -10774,7 +10846,7 @@ var REVIEW_REASONS = {
10774
10846
  };
10775
10847
  function reasonFor(seType, status, flaggedOff) {
10776
10848
  if (flaggedOff && FLAG_OFF_REASONS[seType]) return FLAG_OFF_REASONS[seType];
10777
- return REVIEW_REASONS[seType] ?? (status === "template" ? "Imported as a generic template stub." : "No native mapping yet \u2014 imported as a labelled text placeholder.");
10849
+ return REVIEW_REASONS[seType] ?? (status === "template" ? "Imported as a generic template stub." : "No native mapping yet. imported as a labelled text placeholder.");
10778
10850
  }
10779
10851
  function buildSEBootstrapDescription(overlayId, sourceUrl) {
10780
10852
  const head = overlayId ? `Imported from StreamElements overlay ${overlayId}` : "Imported from StreamElements";
@@ -10804,7 +10876,7 @@ async function fetchSEBootstrap({
10804
10876
  }) {
10805
10877
  if (!apikey) {
10806
10878
  throw new Error(
10807
- "Missing access token. Use the public preview URL (Overlay menu \u2192 Get URL \u2192 Public link) \u2014 editor URLs do not include a token."
10879
+ "Missing access token. Use the public preview URL (Overlay menu \u2192 Get URL \u2192 Public link). editor URLs do not include a token."
10808
10880
  );
10809
10881
  }
10810
10882
  const result = await fetchWithProxyFallback(buildBootstrapUrl(overlayId), {
@@ -10815,7 +10887,7 @@ async function fetchSEBootstrap({
10815
10887
  let detail = "";
10816
10888
  try {
10817
10889
  const body = JSON.parse(await result.errorBody());
10818
- detail = body?.message ? ` \u2014 ${body.message}` : "";
10890
+ detail = body?.message ? `. ${body.message}` : "";
10819
10891
  } catch {
10820
10892
  }
10821
10893
  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}`;
@@ -10957,17 +11029,17 @@ function importSEBootstrap(bootstrap, options = {}) {
10957
11029
  }
10958
11030
  if (coverage.mappings.some((m) => m.status === "placeholder")) {
10959
11031
  coverage.notes.push(
10960
- "Some widgets imported as placeholder text layers \u2014 see SE/05-widget-to-module-mapping.md for status."
11032
+ "Some widgets imported as placeholder text layers. see SE/05-widget-to-module-mapping.md for status."
10961
11033
  );
10962
11034
  }
10963
11035
  if (coverage.mappings.some((m) => m.seType === "se-widget-merch-goal")) {
10964
11036
  coverage.notes.push(
10965
- "Merch-goal widgets imported as generic goals \u2014 see SE/03-missing-variables.md \xA78 for the missing data source."
11037
+ "Merch-goal widgets imported as generic goals. see SE/03-missing-variables.md \xA78 for the missing data source."
10966
11038
  );
10967
11039
  }
10968
11040
  if (coverage.mappings.some((m) => m.seType === "se-widget-custom-event-list")) {
10969
11041
  coverage.notes.push(
10970
- "\u26A0 Custom widgets are experimental \u2014 they run under the SE compatibility shim (Embed.tsx) which handles most SE APIs but may not cover every edge case. Verify each custom layer renders correctly in the editor."
11042
+ "\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."
10971
11043
  );
10972
11044
  }
10973
11045
  const overlay = {
@@ -11112,7 +11184,7 @@ function buildAIPromptForSEWidget(widget, canvas) {
11112
11184
  const fullVariables = JSON.stringify(variables, null, 2);
11113
11185
  const truncated = fullVariables.length > VARIABLES_BUDGET;
11114
11186
  const variablesJson = truncated ? `${fullVariables.slice(0, VARIABLES_BUDGET)}
11115
- \u2026[truncated \u2014 ${fullVariables.length - VARIABLES_BUDGET} more bytes elided]` : fullVariables;
11187
+ \u2026[truncated. ${fullVariables.length - VARIABLES_BUDGET} more bytes elided]` : fullVariables;
11116
11188
  return [
11117
11189
  "Recreate a StreamElements overlay widget as a Lumia Stream custom overlay (HTML + CSS + JS).",
11118
11190
  "",
@@ -11120,14 +11192,14 @@ function buildAIPromptForSEWidget(widget, canvas) {
11120
11192
  listener ? `Primary event listener: ${listener}` : "",
11121
11193
  listeners ? `Subscribes to events: ${listeners.join(", ")}` : "",
11122
11194
  `Canvas: ${landing.width} \xD7 ${landing.height}`,
11123
- truncated ? "Note: the widget config below is truncated \u2014 focus on visible/structural fields and ignore tail content you cannot see." : "",
11195
+ truncated ? "Note: the widget config below is truncated. focus on visible/structural fields and ignore tail content you cannot see." : "",
11124
11196
  "",
11125
11197
  "Widget configuration (JSON):",
11126
11198
  "```json",
11127
11199
  variablesJson,
11128
11200
  "```",
11129
11201
  "",
11130
- "Constraints \u2014 your output must run inside a Lumia Custom Overlay sandboxed iframe:",
11202
+ "Constraints. your output must run inside a Lumia Custom Overlay sandboxed iframe:",
11131
11203
  '- Use `window.Overlay.on("alert", handler)` / `window.Overlay.on("chat", handler)` to receive Lumia events.',
11132
11204
  "- Lumia template variables use double-brace syntax: {{username}}, {{amount}}, {{message}}, {{currency}}.",
11133
11205
  "- 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.0",
3
+ "version": "0.6.2",
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.0",
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",