@lumiastream/ui 0.2.8-alpha.1 → 0.2.8-alpha.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 CHANGED
@@ -508,7 +508,7 @@ import FormControlLabel from "@mui/material/FormControlLabel";
508
508
  import { forwardRef as forwardRef2 } from "react";
509
509
  import { useController, useFormContext } from "react-hook-form";
510
510
  import classNames4 from "classnames";
511
- import { jsx as jsx5 } from "react/jsx-runtime";
511
+ import { Fragment, jsx as jsx5 } from "react/jsx-runtime";
512
512
  var LSCheckbox = forwardRef2(({ className = "", checked, inputRef, slotProps, ...props }, ref) => {
513
513
  const CheckboxComponent = Checkbox;
514
514
  return /* @__PURE__ */ jsx5(
@@ -539,8 +539,31 @@ function LSCheckboxField({
539
539
  checkboxProps
540
540
  }) {
541
541
  const formContext = useFormContext();
542
- const resolvedControl = control ?? formContext.control;
543
- const { field } = useController({ name, control: resolvedControl });
542
+ const resolvedControl = control ?? formContext?.control;
543
+ if (!resolvedControl) return /* @__PURE__ */ jsx5(Fragment, {});
544
+ return /* @__PURE__ */ jsx5(
545
+ LSCheckboxFieldInner,
546
+ {
547
+ name,
548
+ label,
549
+ control: resolvedControl,
550
+ className,
551
+ disabled,
552
+ labelPlacement,
553
+ checkboxProps
554
+ }
555
+ );
556
+ }
557
+ function LSCheckboxFieldInner({
558
+ name,
559
+ label,
560
+ control,
561
+ className = "",
562
+ disabled,
563
+ labelPlacement = "end",
564
+ checkboxProps
565
+ }) {
566
+ const { field } = useController({ name, control });
544
567
  return /* @__PURE__ */ jsx5(
545
568
  FormControlLabel,
546
569
  {
@@ -1045,7 +1068,7 @@ import Search from "@mui/icons-material/Search";
1045
1068
  import KeyboardArrowDown2 from "@mui/icons-material/KeyboardArrowDown";
1046
1069
  import Tooltip from "@mui/material/Tooltip";
1047
1070
  import {
1048
- Fragment,
1071
+ Fragment as Fragment2,
1049
1072
  createContext,
1050
1073
  forwardRef as forwardRef4,
1051
1074
  useCallback as useCallback2,
@@ -1055,7 +1078,7 @@ import {
1055
1078
  useRef as useRef3,
1056
1079
  useState as useState4
1057
1080
  } from "react";
1058
- import { Fragment as Fragment2, jsx as jsx11, jsxs as jsxs5 } from "react/jsx-runtime";
1081
+ import { Fragment as Fragment3, jsx as jsx11, jsxs as jsxs5 } from "react/jsx-runtime";
1059
1082
  import { createElement } from "react";
1060
1083
  var LSVariableInputContext = createContext({});
1061
1084
  var LSVariableInputProvider = ({
@@ -1799,7 +1822,7 @@ var LSVariableInputField = forwardRef4((props, ref) => {
1799
1822
  const fullToken = `${rootToken}.${node.relativePath}`;
1800
1823
  const expanded = node.isExpandable && isPathExpanded(fullToken);
1801
1824
  const { displayValue, fullValue } = getVariableValuePreview(node.value);
1802
- return /* @__PURE__ */ jsxs5(Fragment, { children: [
1825
+ return /* @__PURE__ */ jsxs5(Fragment2, { children: [
1803
1826
  /* @__PURE__ */ jsxs5(
1804
1827
  "div",
1805
1828
  {
@@ -1938,7 +1961,7 @@ var LSVariableInputField = forwardRef4((props, ref) => {
1938
1961
  const expanded = expandable && isPathExpanded(definition.name);
1939
1962
  const isLast = idx === filteredAllowedVariables.length - 1;
1940
1963
  const rowCls = `ls-variable-picker__row${isLast && !expanded ? " ls-variable-picker__row--no-border" : ""}`;
1941
- return /* @__PURE__ */ jsxs5(Fragment, { children: [
1964
+ return /* @__PURE__ */ jsxs5(Fragment2, { children: [
1942
1965
  /* @__PURE__ */ jsxs5(
1943
1966
  "div",
1944
1967
  {
@@ -1978,7 +2001,7 @@ var LSVariableInputField = forwardRef4((props, ref) => {
1978
2001
  const tree = systemVariableValueTrees.get(variable.name);
1979
2002
  const expandable = !!tree && tree.length > 0;
1980
2003
  const expanded = expandable && isPathExpanded(rootToken);
1981
- return /* @__PURE__ */ jsxs5(Fragment, { children: [
2004
+ return /* @__PURE__ */ jsxs5(Fragment2, { children: [
1982
2005
  /* @__PURE__ */ jsxs5(
1983
2006
  "div",
1984
2007
  {
@@ -2028,7 +2051,7 @@ var LSVariableInputField = forwardRef4((props, ref) => {
2028
2051
  ref
2029
2052
  }
2030
2053
  );
2031
- return /* @__PURE__ */ jsxs5(Fragment2, { children: [
2054
+ return /* @__PURE__ */ jsxs5(Fragment3, { children: [
2032
2055
  /* @__PURE__ */ jsx11(
2033
2056
  Popover,
2034
2057
  {
@@ -2212,7 +2235,7 @@ var VariableInputTextField = forwardRef4(
2212
2235
  ...paramsSlotInputProps,
2213
2236
  ...paramsInputProps,
2214
2237
  startAdornment,
2215
- endAdornment: /* @__PURE__ */ jsxs5(Fragment2, { children: [
2238
+ endAdornment: /* @__PURE__ */ jsxs5(Fragment3, { children: [
2216
2239
  endAdornment ?? null,
2217
2240
  showVariableIcon ? /* @__PURE__ */ jsx11(
2218
2241
  Tooltip,
@@ -3126,7 +3149,11 @@ var SE_WIDGET_TYPE_LABELS = {
3126
3149
  "se-widget-hype-cup": "Hype Cup",
3127
3150
  "se-widget-custom-event-list": "Custom widget",
3128
3151
  "se-widget-credit-roll": "Credits",
3129
- "se-widget-media-share": "Media share"
3152
+ "se-widget-media-share": "Media share",
3153
+ // Imports as an empty slideshow — Lumia has no Fourthwall catalog API, so
3154
+ // the streamer has to add product images by hand. The friendly title makes
3155
+ // that obvious in the layers panel; see REVIEW_REASONS for the full prompt.
3156
+ "se-widget-merch-products-rotator": "Merch products (add images manually)"
3130
3157
  };
3131
3158
  function defaultLabelForSEWidget(widget) {
3132
3159
  const trimmed = typeof widget.name === "string" ? widget.name.trim() : "";
@@ -3273,7 +3300,22 @@ var LUMIA_DEFAULT_SIZES = {
3273
3300
  emotebox: { width: 400, height: 400 },
3274
3301
  nowplaying: { width: 600, height: 250 },
3275
3302
  spotify: { width: 600, height: 250 },
3276
- youtubemusic: { width: 600, height: 250 }
3303
+ youtubemusic: { width: 600, height: 250 },
3304
+ tipjar: { width: 600, height: 700 },
3305
+ // Hype Train: wide-and-short by default to fit a horizontal progress bar.
3306
+ // All four built-in themes (subtle / train / rocket / inferno) render
3307
+ // fine at this aspect — rocket internally uses a vertical gauge but
3308
+ // still works at this footprint. Users can resize per their overlay.
3309
+ hypetrain: { width: 740, height: 140 },
3310
+ // Stream Boss: matches SE's bit-boss default (440×86) for a 1:1 import
3311
+ // experience, lightly padded so Lumia's Card-theme avatar fits without
3312
+ // resizing. Users can swap to Bar-Only and shrink height further.
3313
+ streamboss: { width: 480, height: 100 },
3314
+ // Song Request: SE's media-share default is 480×292. Lumia's now-playing
3315
+ // card + 5-item queue list needs a bit more vertical room, so we add ~80px
3316
+ // for the queue list. Users can hide the queue list and shrink back to
3317
+ // just the card if they want SE's exact footprint.
3318
+ songrequest: { width: 480, height: 380 }
3277
3319
  };
3278
3320
  function translateVariationCondition(seType, seCondition, requirement) {
3279
3321
  if (seType === "gift") return { conditionType: "IS_GIFT", condition: 1 };
@@ -3370,6 +3412,17 @@ function mapVideo(widget) {
3370
3412
  }
3371
3413
  });
3372
3414
  }
3415
+ var LUMIA_SNOW_WEBM = "https://storage.lumiastream.com/overlays/global/seasonal/snow.webm";
3416
+ function mapSnow(widget) {
3417
+ return buildUnit(widget, "video", {
3418
+ content: {
3419
+ src: LUMIA_SNOW_WEBM,
3420
+ volume: 0,
3421
+ loop: true,
3422
+ muted: true
3423
+ }
3424
+ });
3425
+ }
3373
3426
  function mapReadout(widget, fallbackVar) {
3374
3427
  const explicitListener = widget.variables?.listener;
3375
3428
  const explicit = typeof explicitListener === "string" ? explicitListener : null;
@@ -3539,6 +3592,9 @@ function currentForListener(listener) {
3539
3592
  if (listener.startsWith("subscriber-")) return "{{twitch_total_subscriber_count}}";
3540
3593
  if (listener.startsWith("cheer-")) return "{{twitch_total_bits_count}}";
3541
3594
  if (listener.startsWith("tip-")) return "{{total_donation_amount}}";
3595
+ if (listener === "merch-goal-orders") return "{{fourthwall_total_order_count}}";
3596
+ if (listener === "merch-goal-items") return "{{fourthwall_total_items_count}}";
3597
+ if (listener === "merch-goal-total") return "{{fourthwall_total_order_amount}}";
3542
3598
  if (listener.startsWith("merch-")) return "{{fourthwall_total_order_count}}";
3543
3599
  return "0";
3544
3600
  }
@@ -3639,77 +3695,6 @@ function mapCustom(widget) {
3639
3695
  }
3640
3696
  });
3641
3697
  }
3642
- var SEASONAL_TEMPLATE_HTML = '<canvas id="particles"></canvas>';
3643
- var SEASONAL_TEMPLATE_CSS = `html, body { margin: 0; padding: 0; width: 100%; height: 100%; background: transparent; overflow: hidden; }
3644
- #particles { position: fixed; inset: 0; width: 100%; height: 100%; pointer-events: none; }`;
3645
- var SEASONAL_TEMPLATE_JS = `const SEASONS = {
3646
- snow: { emoji: '\\u2744', count: 80, sizeMin: 14, sizeMax: 28, gravity: 1.0, drift: 0.6, sway: 1.2 },
3647
- halloween: { emoji: '\\ud83d\\udc7b', count: 25, sizeMin: 28, sizeMax: 44, gravity: 0.6, drift: 0.4, sway: 0.8 },
3648
- xmas: { emoji: '\\u2744', count: 120, sizeMin: 16, sizeMax: 32, gravity: 1.1, drift: 0.7, sway: 1.3 },
3649
- valentine: { emoji: '\\u2764\\ufe0f', count: 50, sizeMin: 18, sizeMax: 34, gravity: 0.8, drift: 0.5, sway: 1.0 },
3650
- easter: { emoji: '\\ud83e\\udd5a', count: 30, sizeMin: 22, sizeMax: 38, gravity: 0.9, drift: 0.3, sway: 0.4 },
3651
- worldcup: { emoji: '\\u26bd', count: 25, sizeMin: 24, sizeMax: 40, gravity: 1.1, drift: 0.4, sway: 0.5 },
3652
- };
3653
- const cfg = SEASONS[(window.DATA && window.DATA.season) || 'snow'] || SEASONS.snow;
3654
- const canvas = document.getElementById('particles');
3655
- const ctx = canvas.getContext('2d');
3656
- const dpr = window.devicePixelRatio || 1;
3657
- function resize() {
3658
- canvas.width = window.innerWidth * dpr;
3659
- canvas.height = window.innerHeight * dpr;
3660
- canvas.style.width = window.innerWidth + 'px';
3661
- canvas.style.height = window.innerHeight + 'px';
3662
- ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
3663
- }
3664
- resize();
3665
- window.addEventListener('resize', resize);
3666
- const particles = [];
3667
- for (let i = 0; i < cfg.count; i++) {
3668
- particles.push({
3669
- x: Math.random() * window.innerWidth,
3670
- y: Math.random() * window.innerHeight - window.innerHeight,
3671
- size: cfg.sizeMin + Math.random() * (cfg.sizeMax - cfg.sizeMin),
3672
- speed: 0.5 + Math.random() * 1.5,
3673
- phase: Math.random() * Math.PI * 2,
3674
- });
3675
- }
3676
- function tick(now) {
3677
- ctx.clearRect(0, 0, window.innerWidth, window.innerHeight);
3678
- const t = now / 1000;
3679
- for (const p of particles) {
3680
- p.y += p.speed * cfg.gravity;
3681
- const sway = Math.sin(t + p.phase) * cfg.sway;
3682
- if (p.y > window.innerHeight + 40) {
3683
- p.y = -40;
3684
- p.x = Math.random() * window.innerWidth;
3685
- }
3686
- ctx.font = p.size + 'px serif';
3687
- ctx.fillText(cfg.emoji, p.x + sway * 20, p.y);
3688
- }
3689
- requestAnimationFrame(tick);
3690
- }
3691
- requestAnimationFrame(tick);`;
3692
- function mapSeasonal(widget, season) {
3693
- return buildUnit(widget, "custom", {
3694
- content: {
3695
- codeId: `seasonal-${season}-${widget.id}`,
3696
- html: SEASONAL_TEMPLATE_HTML,
3697
- css: SEASONAL_TEMPLATE_CSS,
3698
- js: SEASONAL_TEMPLATE_JS,
3699
- configs: [
3700
- {
3701
- key: "season",
3702
- label: "Season",
3703
- type: "dropdown",
3704
- value: season,
3705
- options: { snow: "Snow", halloween: "Halloween", xmas: "Christmas", valentine: "Valentine's", easter: "Easter", worldcup: "World Cup" }
3706
- }
3707
- ],
3708
- data: { season },
3709
- flavor: "lumia-seasonal"
3710
- }
3711
- });
3712
- }
3713
3698
 
3714
3699
  // src/se-import/mappers/misc.ts
3715
3700
  function mapChatbox(widget) {
@@ -4012,6 +3997,49 @@ function mapNowPlaying(widget) {
4012
3997
  }
4013
3998
  });
4014
3999
  }
4000
+ function mapMediaShare(widget) {
4001
+ const v = widget.variables ?? {};
4002
+ return buildUnit(widget, "songrequest", {
4003
+ content: {
4004
+ version: 1,
4005
+ // SE always shows the now-playing card; honour its cosmetic toggles
4006
+ // for the rest. `showTitle` defaults true; the others default to
4007
+ // SE's documented behavior (tipper visible, stats hidden).
4008
+ showNowPlayingCard: true,
4009
+ showQueueList: true,
4010
+ showSkipButton: true,
4011
+ showPauseButton: true,
4012
+ showTitle: v.showTitle !== false,
4013
+ showTipper: v.showTipper !== false,
4014
+ showAmount: v.showAmount === true,
4015
+ showStats: v.showStats === true,
4016
+ // SE doesn't have a queue-size knob — pick a sensible default
4017
+ // matching what fits in the SE widget's ~292px tall footprint.
4018
+ maxQueueItemsVisible: 5,
4019
+ // SE color knobs translate directly. Fall back to Lumia's defaults
4020
+ // (purple accent, near-black card background) if SE values are
4021
+ // missing — matches what the module ships with for native creates.
4022
+ accentColor: v.accentColor ?? "#9146ff",
4023
+ primaryColor: v.primaryColor ?? "#1a1a1a",
4024
+ // SE's widget hides the YT player by default; mirror that for
4025
+ // audio-only overlay use.
4026
+ playerVisible: false,
4027
+ defaultVolume: 70,
4028
+ autoAdvanceOnEnd: true,
4029
+ // SE-only knob preserved for round-trip provenance; Lumia ignores
4030
+ // it (LumiaStream auto-advance fires on `durationSeconds + grace`).
4031
+ se_timeLeft: v.timeLeft === true
4032
+ },
4033
+ css: {
4034
+ fontFamily: "Roboto",
4035
+ fontWeight: "bold",
4036
+ color: "#ffffff",
4037
+ textShadow: "rgb(0, 0, 0) 1px 1px 1px",
4038
+ background: "transparent",
4039
+ fontSize: 18
4040
+ }
4041
+ });
4042
+ }
4015
4043
  var SE_KAPPAGEN_EVENT_TO_LUMIA_ALERT = {
4016
4044
  follower: "twitch-follower",
4017
4045
  subscriber: "twitch-subscriber",
@@ -4155,6 +4183,75 @@ function mapGiveaway(widget) {
4155
4183
  }
4156
4184
  });
4157
4185
  }
4186
+ var HYPETRAIN_LISTENER_KEYS = /* @__PURE__ */ new Set([
4187
+ "hypetrain-latest",
4188
+ "hypetrain-level-progress",
4189
+ "hypetrain-level-goal",
4190
+ "hypetrain-total",
4191
+ "hypetrain-latest-top-contributors",
4192
+ // SE alert-name variants some community widgets subscribe to as well.
4193
+ "hypetrain-start",
4194
+ "hypetrain-end",
4195
+ "hypetrain-cooldown-start",
4196
+ "hypetrain-cooldown-end"
4197
+ ]);
4198
+ function isHypetrainCustomWidget(widget) {
4199
+ const enabled = [];
4200
+ const listeners = widget.listeners;
4201
+ if (listeners && typeof listeners === "object" && !Array.isArray(listeners)) {
4202
+ for (const [k, v] of Object.entries(listeners)) {
4203
+ if (v) enabled.push(k);
4204
+ }
4205
+ } else if (Array.isArray(listeners)) {
4206
+ enabled.push(...listeners);
4207
+ }
4208
+ const explicit = widget.listener;
4209
+ if (typeof explicit === "string" && explicit && !enabled.includes(explicit)) {
4210
+ enabled.push(explicit);
4211
+ }
4212
+ if (enabled.length === 0) return false;
4213
+ return enabled.every((listener) => HYPETRAIN_LISTENER_KEYS.has(listener));
4214
+ }
4215
+ function mapHypetrain(widget) {
4216
+ const v = widget.variables ?? {};
4217
+ return buildUnit(widget, "hypetrain", {
4218
+ content: {
4219
+ // Twitch-native is the right default — SE hype-train widgets always
4220
+ // reflected the Twitch native train. Users can flip to 'synthetic'
4221
+ // or 'hybrid' from Settings if they want cross-platform pooling.
4222
+ sourceMode: "twitch-native",
4223
+ sources: null,
4224
+ // default money-only whitelist; ignored in twitch-native anyway
4225
+ customValues: {},
4226
+ pretrainThreshold: 5,
4227
+ pretrainDurationMs: 5 * 60 * 1e3,
4228
+ levelDurationMs: 5 * 60 * 1e3,
4229
+ levelStep: 5,
4230
+ levelThresholds: [],
4231
+ // Pick a moderately energetic default theme — SE hype-train widgets
4232
+ // were never subtle. Users can switch in Settings.
4233
+ theme: "train",
4234
+ primaryColor: typeof v.primaryColor === "string" ? v.primaryColor : "#ff6b00",
4235
+ accentColor: typeof v.accentColor === "string" ? v.accentColor : "#ffd84d",
4236
+ textColor: typeof v.textColor === "string" ? v.textColor : "#ffffff",
4237
+ showLevel: true,
4238
+ showTotal: true,
4239
+ showTimer: true,
4240
+ showTopContributor: true,
4241
+ showLeaderboard: true,
4242
+ leaderboardLimit: 5,
4243
+ leaderboardDurationMs: 30 * 1e3,
4244
+ levelUpSoundUrl: "https://storage.lumiastream.com/overlays/lumia/audio/crowdClap.mp3",
4245
+ trainEndSoundUrl: "https://storage.lumiastream.com/overlays/lumia/audio/crowdClap.mp3",
4246
+ audioVolume: 0.5,
4247
+ // SE config preserved verbatim as provenance — no Lumia equivalent for
4248
+ // most of these knobs (per-widget animation tunables, sound URLs the
4249
+ // streamer might want to migrate manually, etc.). Users can inspect
4250
+ // these via the overlay JSON if they want to recreate a specific look.
4251
+ se_variables: v
4252
+ }
4253
+ });
4254
+ }
4158
4255
  function mapUnsupportedAsText(widget) {
4159
4256
  return buildUnit(widget, "text", {
4160
4257
  content: {
@@ -4172,44 +4269,258 @@ function mapUnsupportedAsText(widget) {
4172
4269
  });
4173
4270
  }
4174
4271
 
4272
+ // src/se-import/mappers/hypecup.ts
4273
+ var SE_TYPE_TO_CATEGORY = {
4274
+ cheer: "bits",
4275
+ sub: "subs",
4276
+ follower: "follows",
4277
+ raid: "raids",
4278
+ tip: "tips"
4279
+ };
4280
+ function liftSESpritesIntoTiers(types) {
4281
+ const out = {};
4282
+ if (!types) return out;
4283
+ for (const [seKey, block] of Object.entries(types)) {
4284
+ const cat = SE_TYPE_TO_CATEGORY[seKey];
4285
+ if (!cat) continue;
4286
+ const variations = block?.variations ?? [];
4287
+ const tiers = variations.filter((v) => typeof v?.src === "string" && v.src.length > 0).map((v) => ({
4288
+ minAmount: typeof v.amount === "number" && v.amount > 0 ? v.amount : 1,
4289
+ imageUrl: v.src,
4290
+ scale: typeof v.cheerSize === "number" && v.cheerSize > 0 ? v.cheerSize : 1
4291
+ }));
4292
+ if (tiers.length > 0) out[cat] = tiers;
4293
+ }
4294
+ return out;
4295
+ }
4296
+ var LISTENER_TO_CATEGORY = {
4297
+ "follower-latest": "follows",
4298
+ "subscriber-latest": "subs",
4299
+ "tip-latest": "tips",
4300
+ "cheer-latest": "bits",
4301
+ "raid-latest": "raids"
4302
+ };
4303
+ function categoriesFromListeners(listeners) {
4304
+ const events = {
4305
+ tips: false,
4306
+ bits: false,
4307
+ subs: false,
4308
+ resubs: false,
4309
+ gifts: false,
4310
+ superchats: false,
4311
+ superstickers: false,
4312
+ kicks: false,
4313
+ charity: false,
4314
+ follows: false,
4315
+ raids: false,
4316
+ redemptions: false,
4317
+ chat: false
4318
+ };
4319
+ if (!listeners) return events;
4320
+ const onListeners = Array.isArray(listeners) ? listeners : Object.entries(listeners).filter(([, on]) => on === true).map(([k]) => k);
4321
+ for (const l of onListeners) {
4322
+ const cat = LISTENER_TO_CATEGORY[l];
4323
+ if (cat) events[cat] = true;
4324
+ }
4325
+ if (events.subs) events.resubs = true;
4326
+ return events;
4327
+ }
4328
+ function mapHypeCup(widget) {
4329
+ const v = widget.variables ?? {};
4330
+ const cupX = typeof v.cupPosition?.x === "number" ? clamp01(v.cupPosition.x) : 0.5;
4331
+ const cupY = typeof v.cupPosition?.y === "number" ? clamp01(v.cupPosition.y) : 0.7;
4332
+ const bounce = typeof v.bounce === "number" ? Math.min(1, Math.max(0, v.bounce)) : 0.3;
4333
+ const maxBodies = typeof v.recentEvents === "number" && v.recentEvents > 0 ? Math.min(300, Math.max(10, v.recentEvents)) : 80;
4334
+ const duration = typeof v.duration === "number" && v.duration > 0 ? Math.min(15, Math.max(1, v.duration)) : 3;
4335
+ const sprites = liftSESpritesIntoTiers(v.types);
4336
+ return buildUnit(widget, "tipjar", {
4337
+ content: {
4338
+ version: 1,
4339
+ // Phase 1 ships a single preset; SE's per-cup polygon set isn't lifted
4340
+ // yet (it'd require multi-cup support which we deliberately deferred).
4341
+ // The streamer can swap a custom cup image + wireframe via Settings.
4342
+ preset: "glass-mug",
4343
+ customCup: null,
4344
+ cup: {
4345
+ x: cupX,
4346
+ y: cupY,
4347
+ scale: 1,
4348
+ opacity: 1
4349
+ },
4350
+ physics: {
4351
+ gravity: 1,
4352
+ bounce,
4353
+ friction: 0.06,
4354
+ maxBodies
4355
+ },
4356
+ tokens: {
4357
+ defaultRadius: 26
4358
+ },
4359
+ events: categoriesFromListeners(widget.listeners),
4360
+ chat: {
4361
+ useViewerAvatars: true
4362
+ },
4363
+ display: {
4364
+ showWireframes: !!v.showWireframes,
4365
+ showEventMessages: !!v.textEnabled,
4366
+ messageDuration: duration
4367
+ },
4368
+ sprites,
4369
+ // Phase 2 defaults for goal/break — disabled by import. Streamer
4370
+ // can enable in Settings; SE has no equivalent so nothing to lift.
4371
+ goal: { active: false, currency: "tips", target: 100, current: 0, readoutTemplate: "{{current}} / {{target}}", textColor: "#ffffff", hitAt: null },
4372
+ break: { enabled: false, command: "!breakjar", rebuildDelay: 5e3 },
4373
+ // SE-only knobs that don't have a Lumia equivalent — preserved as
4374
+ // provenance for future phases (per-cup polygons + enter/movement
4375
+ // physics tweaks). `se_types` removed — now consumed into sprites above.
4376
+ se_enter_from: v.enterFrom ?? null,
4377
+ se_prevent_movement: !!v.preventMovement,
4378
+ se_fullscreen: !!v.fullscreen,
4379
+ se_cups: v.cups ?? null
4380
+ },
4381
+ css: {
4382
+ background: "transparent"
4383
+ }
4384
+ });
4385
+ }
4386
+ function clamp01(n) {
4387
+ if (!Number.isFinite(n)) return 0.5;
4388
+ return Math.min(1, Math.max(0, n));
4389
+ }
4390
+
4391
+ // src/se-import/mappers/streamboss.ts
4392
+ var SE_LISTENER_TO_ALERT = {
4393
+ "follower-latest": "twitch-follower",
4394
+ "subscriber-latest": "twitch-subscriber",
4395
+ "tip-latest": "lumiastream-donation",
4396
+ "cheer-latest": "twitch-bits",
4397
+ "raid-latest": "twitch-raid"
4398
+ // `purchase-latest` and `subscriber-gifted-latest` are intentionally absent.
4399
+ // SE rolled gifted subs into `subscriber-latest`; Lumia splits them via
4400
+ // extraSettings.isGift, which the runtime already handles consistently.
4401
+ };
4402
+ var SE_LISTENER_MODE = {
4403
+ "follower-latest": "flat",
4404
+ "subscriber-latest": "flat",
4405
+ "tip-latest": "multiplier",
4406
+ // 100 means "100 dmg per $"
4407
+ "cheer-latest": "multiplier",
4408
+ // 1 means "1 dmg per bit"
4409
+ "raid-latest": "multiplier"
4410
+ // 10 means "10 dmg per viewer"
4411
+ };
4412
+ function listenerIsOn(listeners, key) {
4413
+ if (!listeners) return false;
4414
+ if (Array.isArray(listeners)) return listeners.includes(key);
4415
+ return listeners[key] === true;
4416
+ }
4417
+ function mapStreamBoss(widget) {
4418
+ const v = widget.variables ?? {};
4419
+ const baseHp = typeof v.baseHP === "number" && v.baseHP > 0 ? Math.round(v.baseHP) : 1e3;
4420
+ const seDamage = v.damage ?? {};
4421
+ const damageMap = {};
4422
+ const selectedEvents = [];
4423
+ for (const [seListener, alertKey] of Object.entries(SE_LISTENER_TO_ALERT)) {
4424
+ if (!listenerIsOn(widget.listeners, seListener)) continue;
4425
+ const raw = seDamage[seListener];
4426
+ const value = typeof raw === "number" && Number.isFinite(raw) && raw > 0 ? raw : null;
4427
+ if (value === null) continue;
4428
+ damageMap[alertKey] = { value, mode: SE_LISTENER_MODE[seListener] };
4429
+ selectedEvents.push(alertKey);
4430
+ }
4431
+ const barColor = typeof v.colors?.frame === "string" && v.colors.frame ? v.colors.frame : "#2bff00";
4432
+ return buildUnit(widget, "streamboss", {
4433
+ content: {
4434
+ version: 1,
4435
+ // Theme: SE bit-boss with `showImage: true` reads as the Card layout
4436
+ // (avatar + name + bar). When images are hidden it's effectively
4437
+ // our Bar-Only theme.
4438
+ theme: v.showImage === false ? "bar-only" : "card",
4439
+ initialBossName: "Stream Boss",
4440
+ // SE has no stage cycling — every takeover resets to baseHP. We
4441
+ // honor that by shipping a single-element healthStages array; the
4442
+ // runtime cycles modulo length so a one-element array stays put.
4443
+ healthStages: [baseHp],
4444
+ currentBoss: "Stream Boss",
4445
+ currentBossAvatar: "",
4446
+ currentHP: baseHp,
4447
+ maxHP: baseHp,
4448
+ currentStageIndex: 0,
4449
+ damageMap,
4450
+ // Concrete selectedEvents means "user has opted in to exactly these
4451
+ // events". Empty arrays would disable everything — only happens if
4452
+ // SE had all listeners off, which mirrors what the imported overlay
4453
+ // should do.
4454
+ selectedEvents,
4455
+ bossHealsOnEventTrigger: v.selfHeal !== false,
4456
+ newBossCommand: "!newboss",
4457
+ eventQueueDelayMs: 3e3,
4458
+ defeatedDelayMs: 3e3,
4459
+ showAvatar: v.showImage !== false,
4460
+ showFloatingNumbers: true,
4461
+ showEventMessages: v.showText !== false,
4462
+ barColor,
4463
+ barBackgroundColor: "#333333",
4464
+ attackColor: "#ff007f",
4465
+ healColor: "#00ff00",
4466
+ // Phase 2 defaults — off by default so imports stay close to
4467
+ // SE's behaviour and don't surprise the streamer with new effects.
4468
+ lastResetAt: 0,
4469
+ playSounds: true,
4470
+ audioVolume: 50,
4471
+ damageSounds: [],
4472
+ healSound: "",
4473
+ newBossSound: "",
4474
+ defeatSound: "",
4475
+ megaBossChance: 0,
4476
+ particleScatter: true,
4477
+ showCrown: true,
4478
+ matchBossChatColor: true,
4479
+ bossNameColor: "",
4480
+ botChat: "off",
4481
+ chatAsSelf: false,
4482
+ // SE-only knobs preserved as provenance for future passes. None
4483
+ // have direct Lumia analogs today:
4484
+ // - `right` anchors the boss to the right edge of its bounds;
4485
+ // Lumia uses layer position instead, so this is informational.
4486
+ // - `bossName` controls vertical name position in SE; our themes
4487
+ // don't expose that level of fine-grained tweaking yet.
4488
+ // - `mode` SE's gameplay mode hint ('overkill', etc.).
4489
+ se_right: !!v.right,
4490
+ se_bossNamePosition: typeof v.bossName === "string" ? v.bossName : null,
4491
+ se_mode: typeof v.mode === "string" ? v.mode : null,
4492
+ se_showName: v.showName !== false
4493
+ },
4494
+ css: {
4495
+ background: "transparent"
4496
+ }
4497
+ });
4498
+ }
4499
+
4175
4500
  // src/se-import/feature-flags.ts
4176
4501
  var SE_IMPORT_FLAGS = {
4177
4502
  // (Removed) IMPORT_CUSTOM_EMBED: custom-event-list now routes to native eventlist
4178
4503
  // by default with a one-click "switch to custom HTML" option in the review step.
4179
4504
  // (Removed) IMPORT_MERCH_GOAL: Fourthwall integration provides session/total order
4180
4505
  // counters now (see fourthwall.manager.ts ORDER_PLACED handler); merch goal maps
4181
- // to {{fourthwall_total_order_count}} via the goal mapper.
4182
- // Merch products rotator Lumia has no Fourthwall product-list API yet. The
4183
- // Fourthwall integration is webhook-driven (events flow in) but doesn't fetch
4184
- // the user's shop catalog. Adding it requires a separate Fourthwall API auth
4185
- // flow + product-fetch endpoint + periodic refresh. Until then a static stub
4186
- // is unhelpful, so users are routed to review for AI/skip/marketplace-when-
4187
- // available replacements.
4188
- IMPORT_MERCH_PRODUCTS: false,
4189
- // Seasonal effects (snow/halloween/xmas/valentine/easter/worldcup). The
4190
- // generic canvas-particle template is approximate, not pixel-accurate vs
4191
- // SE's polished versions. Flagging off so users get the option to grab a
4192
- // AI-generated replacement or a curated marketplace seasonal overlay when one
4193
- // is configured.
4194
- IMPORT_SEASONAL: false
4506
+ // to {{fourthwall_total_*}} via the goal mapper (per-listener: orders → order_count,
4507
+ // items items_count, total order_amount).
4508
+ // (Removed) IMPORT_SEASONAL: seasonal widgets now route per-widget into existing
4509
+ // modules (image / slideshow / video) rather than a single approximate canvas-
4510
+ // particle template. See dispatcher.ts SEASONAL_IMAGE_TYPES comment for the
4511
+ // rationale and /SE/05-widget-to-module-mapping.md §D for the current mapping.
4512
+ // (Removed) IMPORT_MERCH_PRODUCTS: the merch-products-rotator now imports as an
4513
+ // empty slideshow with a friendly layer title prompting the user to add product
4514
+ // images manually. This is a degraded but honest UX — better than a placeholder
4515
+ // text layer for the (rare) streamer who uses SE's merch rotator. A Fourthwall
4516
+ // catalog fetcher would unlock fully-automatic import, but that's a multi-day
4517
+ // project gated on a new Fourthwall API auth scope, an endpoint, and a refresh
4518
+ // cadence.
4195
4519
  };
4196
- function isWidgetFlaggedOff(seType) {
4197
- if (seType === "se-widget-merch-products-rotator") return !SE_IMPORT_FLAGS.IMPORT_MERCH_PRODUCTS;
4198
- if (seType === "se-widget-snow" || seType === "se-widget-halloween" || seType === "se-widget-halloween-2019" || seType === "se-widget-xmas" || seType === "se-widget-valentine" || seType === "se-widget-easter" || seType === "se-widget-world-cup") {
4199
- return !SE_IMPORT_FLAGS.IMPORT_SEASONAL;
4200
- }
4520
+ function isWidgetFlaggedOff(_seType) {
4201
4521
  return false;
4202
4522
  }
4203
- var FLAG_OFF_REASONS = {
4204
- "se-widget-merch-products-rotator": "Merch products rotator \u2014 Lumia tracks Fourthwall orders but doesn't fetch the full product catalog yet. Generate a replacement with AI, skip it, or use Marketplace when a curated replacement is configured.",
4205
- "se-widget-snow": "Seasonal effect \u2014 the generic auto-import template is approximate. Generate a replacement with AI, skip it, or use Marketplace when a curated version is configured.",
4206
- "se-widget-halloween": "Seasonal effect \u2014 the generic auto-import template is approximate. Generate a replacement with AI, skip it, or use Marketplace when a curated version is configured.",
4207
- "se-widget-halloween-2019": "Seasonal effect \u2014 the generic auto-import template is approximate. Generate a replacement with AI, skip it, or use Marketplace when a curated version is configured.",
4208
- "se-widget-xmas": "Seasonal effect \u2014 the generic auto-import template is approximate. Generate a replacement with AI, skip it, or use Marketplace when a curated version is configured.",
4209
- "se-widget-valentine": "Seasonal effect \u2014 the generic auto-import template is approximate. Generate a replacement with AI, skip it, or use Marketplace when a curated version is configured.",
4210
- "se-widget-easter": "Seasonal effect \u2014 the generic auto-import template is approximate. Generate a replacement with AI, skip it, or use Marketplace when a curated version is configured.",
4211
- "se-widget-world-cup": "Seasonal effect \u2014 the generic auto-import template is approximate. Generate a replacement with AI, skip it, or use Marketplace when a curated version is configured."
4212
- };
4523
+ var FLAG_OFF_REASONS = {};
4213
4524
 
4214
4525
  // src/se-import/dispatcher.ts
4215
4526
  var READOUT_TYPES = {
@@ -4238,15 +4549,7 @@ var READOUT_TYPES = {
4238
4549
  // twitch_session_chat_count, so we render it as a text layer with the variable inline.
4239
4550
  "se-widget-botcounter": "twitch_session_chat_count"
4240
4551
  };
4241
- var SEASONAL_TYPES = {
4242
- "se-widget-snow": "snow",
4243
- "se-widget-halloween": "halloween",
4244
- "se-widget-halloween-2019": "halloween",
4245
- "se-widget-xmas": "xmas",
4246
- "se-widget-valentine": "valentine",
4247
- "se-widget-easter": "easter",
4248
- "se-widget-world-cup": "worldcup"
4249
- };
4552
+ var SEASONAL_IMAGE_TYPES = /* @__PURE__ */ new Set(["se-widget-halloween", "se-widget-xmas", "se-widget-valentine", "se-widget-easter", "se-widget-world-cup"]);
4250
4553
  var GOAL_TYPES = /* @__PURE__ */ new Set([
4251
4554
  "se-widget-follower-goal",
4252
4555
  "se-widget-subscriber-goal",
@@ -4287,10 +4590,17 @@ function dispatch(widget) {
4287
4590
  if (t === "se-widget-current-song") return { unit: mapNowPlaying(widget), status: "partial", lumiaType: "nowplaying" };
4288
4591
  if (t === "se-widget-kappagen") return { unit: mapKappagen(widget), status: "partial", lumiaType: "emotealert" };
4289
4592
  if (t === "se-widget-giveaway") return { unit: mapGiveaway(widget), status: "direct", lumiaType: "raffle" };
4290
- if (t === "se-widget-custom-event-list") return { unit: mapCustom(widget), status: "partial", lumiaType: "custom" };
4291
- if (t in SEASONAL_TYPES) {
4292
- return { unit: mapSeasonal(widget, SEASONAL_TYPES[t]), status: "direct", lumiaType: "custom" };
4593
+ if (t === "se-widget-media-share") return { unit: mapMediaShare(widget), status: "partial", lumiaType: "songrequest" };
4594
+ if (t === "se-widget-hype-cup") return { unit: mapHypeCup(widget), status: "partial", lumiaType: "tipjar" };
4595
+ if (t === "se-widget-bit-boss") return { unit: mapStreamBoss(widget), status: "partial", lumiaType: "streamboss" };
4596
+ if (t === "se-widget-hype-train") return { unit: mapHypetrain(widget), status: "partial", lumiaType: "hypetrain" };
4597
+ if (t === "se-widget-custom-event-list" && isHypetrainCustomWidget(widget)) {
4598
+ return { unit: mapHypetrain(widget), status: "partial", lumiaType: "hypetrain" };
4293
4599
  }
4600
+ if (t === "se-widget-custom-event-list") return { unit: mapCustom(widget), status: "partial", lumiaType: "custom" };
4601
+ if (t === "se-widget-snow") return { unit: mapSnow(widget), status: "direct", lumiaType: "video" };
4602
+ if (t === "se-widget-halloween-2019") return { unit: mapSlideshow(widget), status: "direct", lumiaType: "slideshow" };
4603
+ if (SEASONAL_IMAGE_TYPES.has(t)) return { unit: mapImage(widget), status: "direct", lumiaType: "image" };
4294
4604
  return { unit: mapUnsupportedAsText(widget), status: "placeholder", lumiaType: "text" };
4295
4605
  }
4296
4606
  function recordCoverage(coverage, result, seType) {
@@ -4477,7 +4787,10 @@ var SE_WIDGET_TO_MARKETPLACE_CANDIDATES = {
4477
4787
  // value for marketplace replacements since AI generation rarely produces
4478
4788
  // satisfying results for these.
4479
4789
  "se-widget-hype-cup": [],
4480
- "se-widget-bit-boss": [],
4790
+ // Note: `se-widget-bit-boss` is auto-mapped onto the native `streamboss`
4791
+ // module by the dispatcher and no longer needs a marketplace fallback.
4792
+ // Re-add a key here if a community curated boss overlay later supersedes
4793
+ // the native module for specific use cases.
4481
4794
  "se-widget-train": [],
4482
4795
  // Medium-difficulty widgets — possible to AI-generate but a curated overlay
4483
4796
  // will usually look better.
@@ -4487,6 +4800,15 @@ var SE_WIDGET_TO_MARKETPLACE_CANDIDATES = {
4487
4800
  // have curated replacements for.
4488
4801
  "se-widget-custom-event-list": [],
4489
4802
  "se-widget-botcounter": [],
4803
+ // Hype Train — the dispatcher already auto-routes `se-widget-hype-train` and
4804
+ // hypetrain-only custom-event-lists onto the native `hypetrain` module. This
4805
+ // entry stays here for two cases the auto-router can't catch:
4806
+ // - Heavily customized hype-train custom widgets where the native module
4807
+ // would lose too much visual fidelity (custom locomotive art, themed
4808
+ // particles, etc.) and the streamer would prefer a curated alternative.
4809
+ // - Community curated overlays that pair the native hypetrain with extra
4810
+ // decorative layers (e.g. a "depot" background, station signage).
4811
+ "se-widget-hype-train": [],
4490
4812
  // Flagged-off in feature-flags.ts — the auto-import is disabled because
4491
4813
  // Lumia has no merch data source. Marketplace overlays here would be
4492
4814
  // curated static merch-style goals/sliders that don't depend on a feed.
@@ -4619,6 +4941,13 @@ async function fetchUsableChannels(client) {
4619
4941
  const me = await client.get("/kappa/v2/users/current");
4620
4942
  return (me.channels ?? []).filter((c) => c.authorized !== false && c.inactive !== true);
4621
4943
  }
4944
+ async function fetchSEOverlays(client, count = 200) {
4945
+ const res = await client.get(`/kappa/v2/overlays/${client.channelId}?count=${count}`);
4946
+ return res.docs ?? [];
4947
+ }
4948
+ async function fetchSEBootstrapForOverlay(client, overlayId) {
4949
+ return client.get(`/kappa/v2/overlays/${overlayId}/bootstrap?isEditor=false&isMobile=false&isObs=false&isObsLive=false&isXsplit=false`);
4950
+ }
4622
4951
 
4623
4952
  // src/se-import/ui/SEImportWizard.tsx
4624
4953
  import { useCallback as useCallback3, useEffect as useEffect6, useMemo as useMemo5, useRef as useRef4, useState as useState6 } from "react";
@@ -4765,21 +5094,35 @@ function ActiveLayerPreview({ overlay, layerId, CustomEmbed }) {
4765
5094
  // src/se-import/ui/SEImportWizard.css
4766
5095
  styleInject('.se-import,\n.se-import * {\n box-sizing: border-box;\n}\n.se-import {\n --se-bg: #17162a;\n --se-surface: #27264a;\n --se-panel: #171b38;\n --se-panel-2: #202449;\n --se-border: rgba(116, 124, 211, 0.24);\n --se-border-strong: rgba(126, 128, 255, 0.58);\n --se-text: #f5f4ff;\n --se-muted: #bab8d2;\n --se-primary: #ff4076;\n --se-purple: #393853;\n --se-green: #06c96f;\n --se-warn: #f7a42b;\n width: 100%;\n min-width: min(680px, 100%);\n color: var(--se-text);\n font-family: inherit;\n}\n.se-import__shell {\n border-radius: 0;\n padding: 28px 48px 24px;\n}\n.se-import__header {\n display: grid;\n justify-items: center;\n gap: 42px;\n}\n.se-import__title {\n display: inline-flex;\n align-items: center;\n gap: 10px;\n font-size: 20px;\n font-weight: 500;\n}\n.se-import-stepper {\n display: grid;\n width: min(760px, 100%);\n grid-template-columns: repeat(auto-fit, minmax(88px, 1fr));\n}\n.se-import-step {\n position: relative;\n display: grid;\n justify-items: center;\n gap: 12px;\n min-width: 0;\n color: rgba(255, 255, 255, 0.72);\n}\n.se-import-step__label {\n color: var(--se-text);\n font-size: 18px;\n line-height: 1.1;\n}\n.se-import-step__line {\n position: absolute;\n top: 42px;\n left: 50%;\n width: 100%;\n height: 2px;\n background: rgba(255, 255, 255, 0.16);\n}\n.se-import-step:last-child .se-import-step__line {\n display: none;\n}\n.se-import-step__dot {\n z-index: 1;\n display: grid;\n width: 22px;\n height: 22px;\n place-items: center;\n border-radius: 50%;\n background: #47485e;\n color: #fff;\n font-size: 13px;\n font-weight: 700;\n line-height: 1;\n}\n.se-import-step--active .se-import-step__dot {\n background: var(--se-primary);\n}\n.se-import-step--done .se-import-step__dot {\n background: var(--se-green);\n}\n.se-import-step--done .se-import-step__line {\n background: var(--se-green);\n}\n.se-import-step--done + .se-import-step--active .se-import-step__line {\n background:\n linear-gradient(\n 90deg,\n var(--se-green),\n var(--se-primary));\n}\n.se-import__content {\n display: grid;\n min-height: 470px;\n place-items: center;\n padding: 64px 0 28px;\n}\n.se-import-panel {\n width: min(100%, 1080px);\n padding: 28px;\n}\n.se-import-panel--narrow {\n width: min(100%, 1140px);\n}\n.se-import-panel--medium {\n width: min(100%, 880px);\n}\n.se-import-panel--wide {\n width: min(100%, 1000px);\n}\n.se-import-panel--confirm {\n width: min(100%, 800px);\n}\n.se-import-panel h2,\n.se-import-panel h3,\n.se-import-panel p {\n margin: 0;\n}\n.se-import-panel h2 {\n font-size: 26px;\n font-weight: 700;\n line-height: 1.15;\n}\n.se-import-panel > p {\n margin-top: 14px;\n color: var(--se-muted);\n font-size: 16px;\n line-height: 1.45;\n}\n.se-import-field {\n margin-top: 34px;\n}\n.se-import-field .mui-ls-input {\n --ls-control-height: 66px;\n}\n.se-import-field .MuiInputLabel-root {\n color: var(--se-text);\n font-weight: 700;\n}\n.se-import-field .MuiInputLabel-root.Mui-focused {\n color: #dfe1ff;\n}\n.se-import-field .MuiInputBase-root {\n background: rgba(28, 32, 66, 0.85);\n box-shadow: inset 0 0 0 3px rgba(255, 255, 255, 0.04);\n font-size: 18px;\n}\n.se-import-field .MuiOutlinedInput-notchedOutline {\n border-color: var(--se-border-strong) !important;\n}\n.se-import-help {\n display: flex;\n gap: 18px;\n margin-top: 30px;\n border: 1px solid var(--se-purple);\n border-radius: 12px;\n padding: 24px;\n color: var(--white2);\n}\n.se-import-help__icon {\n display: grid;\n width: 30px;\n height: 30px;\n flex: 0 0 auto;\n place-items: center;\n border: 1px solid var(--se-purple);\n border-radius: 50%;\n color: var(--white2);\n font-weight: 800;\n}\n.se-import-help__title {\n color: var(--se-text);\n font-size: 18px;\n font-weight: 700;\n}\n.se-import-help ol {\n margin: 16px 0 0;\n padding-left: 18px;\n font-size: 15px;\n line-height: 1.7;\n}\n.se-import-help a {\n color: var(--primary);\n font-weight: 700;\n}\n.se-import-help--info {\n align-items: center;\n}\n.se-import-section-title {\n display: flex;\n gap: 1rem;\n margin-bottom: 1.5rem;\n}\n.se-import-section-title__num {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 46px;\n height: 46px;\n border: 1px solid var(--se-purple);\n border-radius: 50%;\n font-size: 1.5rem;\n font-weight: 800;\n}\n.se-import-section-title__content {\n display: flex;\n flex-direction: column;\n justify-content: center;\n}\n.se-import-section-title h3 {\n font-size: 18px;\n font-weight: 800;\n}\n.se-import-section-title p {\n margin-top: 4px;\n color: var(--se-muted);\n font-size: 14px;\n}\n.se-import-grid {\n display: grid;\n gap: 8px;\n margin-top: 10px;\n}\n.se-import-grid--four {\n grid-template-columns: repeat(4, minmax(0, 1fr));\n}\n.se-import-grid--two {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n}\n.se-import-stat {\n min-height: 116px;\n border: 1px solid rgba(255, 255, 255, 0.06);\n border-radius: 8px;\n background:\n linear-gradient(\n 135deg,\n rgba(42, 47, 92, 0.9),\n rgba(33, 38, 78, 0.9));\n padding: 24px 22px;\n}\n.se-import-stat--success {\n background:\n linear-gradient(\n 135deg,\n rgba(12, 91, 86, 0.72),\n rgba(32, 65, 76, 0.8));\n border-color: rgba(19, 201, 126, 0.25);\n}\n.se-import-stat--info {\n border-color: rgba(116, 104, 255, 0.8);\n box-shadow: inset 3px 0 0 #6e76ff;\n}\n.se-import-stat--warn,\n.se-import-stat--muted {\n box-shadow: inset 3px 0 0 var(--se-warn);\n}\n.se-import-stat__label {\n color: var(--se-muted);\n font-size: 12px;\n font-weight: 800;\n letter-spacing: 0.04em;\n text-transform: uppercase;\n}\n.se-import-stat__value {\n margin-top: 12px;\n color: #fff;\n font-size: 30px;\n font-weight: 800;\n line-height: 1;\n}\n.se-import-stat__sub {\n margin-top: 8px;\n color: var(--se-muted);\n font-size: 13px;\n}\n.se-import-option {\n display: grid;\n grid-template-columns: auto minmax(0, 1fr) auto auto;\n align-items: center;\n gap: 18px;\n border: 1px solid var(--se-purple);\n border-radius: 12px;\n background:\n linear-gradient(\n 135deg,\n rgba(35, 39, 81, 0.95),\n rgba(31, 35, 73, 0.95));\n box-shadow: inset 3px 0 0 var(--se-primary);\n cursor: pointer;\n padding: 22px;\n margin-bottom: 1rem;\n}\n.se-import-option--disabled {\n cursor: not-allowed;\n opacity: 0.5;\n}\n.se-import-option__icon {\n display: grid;\n width: 26px;\n height: 26px;\n place-items: center;\n border-radius: 50%;\n background: rgba(255, 255, 255, 0.22);\n color: #d8dbff;\n font-weight: 800;\n}\n.se-import-option__body {\n display: grid;\n gap: 8px;\n min-width: 0;\n}\n.se-import-option__title {\n font-size: 17px;\n font-weight: 800;\n}\n.se-import-option__copy {\n color: var(--se-muted);\n font-size: 14px;\n line-height: 1.45;\n}\n.se-import-option__count {\n color: #fff;\n font-size: 16px;\n}\n.se-import-option input {\n position: absolute;\n opacity: 0;\n pointer-events: none;\n}\n.se-import-toggle {\n position: relative;\n width: 48px;\n height: 28px;\n border-radius: 999px;\n box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.14);\n}\n.se-import-toggle::after {\n position: absolute;\n top: 4px;\n left: 4px;\n width: 20px;\n height: 20px;\n border-radius: 50%;\n background: #fff;\n content: "";\n transition: transform 160ms ease;\n}\n.se-import-option input:checked + .se-import-toggle {\n background: var(--primary);\n}\n.se-import-option input:checked + .se-import-toggle::after {\n transform: translateX(20px);\n}\n.se-import-advanced {\n margin-top: 26px;\n border-radius: 10px;\n background: rgba(32, 36, 73, 0.7);\n padding: 24px;\n}\n.se-import-advanced summary {\n cursor: pointer;\n font-size: 18px;\n font-weight: 700;\n}\n.se-import-advanced p {\n margin-top: 10px;\n color: var(--se-muted);\n}\n.se-import-transfer {\n position: relative;\n display: grid;\n width: min(720px, 100%);\n min-height: 190px;\n grid-template-columns: 180px minmax(260px, 1fr) 180px;\n align-items: center;\n gap: 28px;\n margin: 0 auto 60px;\n}\n.se-import-transfer__source {\n position: relative;\n display: grid;\n width: 160px;\n height: 160px;\n place-items: center;\n animation: se-import-source-bounce 1.8s ease-in-out infinite;\n}\n.se-import-transfer__source::before {\n top: -13px;\n left: -13px;\n box-shadow: 155px 0 0 var(--se-primary);\n}\n.se-import-transfer__source::after {\n bottom: -13px;\n left: -13px;\n box-shadow: 155px 0 0 var(--se-primary);\n}\n.se-import-transfer__source img {\n width: 192px;\n height: 192px;\n object-fit: contain;\n}\n.se-import-transfer__source--lumia {\n animation-delay: 0.22s;\n}\n.se-import-transfer__files {\n position: relative;\n height: 130px;\n}\n.se-import-transfer__file {\n position: absolute;\n top: 50%;\n left: -18px;\n width: 56px;\n height: 66px;\n object-fit: contain;\n transform: translateY(-50%);\n filter: drop-shadow(0 10px 12px rgba(8, 9, 25, 0.25));\n animation: se-import-file-fly 2.4s cubic-bezier(0.35, 0, 0.25, 1) infinite;\n}\n.se-import-transfer__file--one {\n animation-delay: 0s;\n}\n.se-import-transfer__file--two {\n animation-delay: 0.38s;\n}\n.se-import-transfer__file--three {\n animation-delay: 0.76s;\n}\n.se-import-transfer__file--four {\n animation-delay: 1.14s;\n}\n@keyframes se-import-source-bounce {\n 0%, 100% {\n transform: translateY(0) scale(1);\n }\n 50% {\n transform: translateY(-8px) scale(1.025);\n }\n}\n@keyframes se-import-file-fly {\n 0% {\n opacity: 0;\n transform: translate(-10px, -50%) scale(0.82) rotate(-8deg);\n }\n 12% {\n opacity: 1;\n }\n 45% {\n transform: translate(145px, calc(-50% - 18px)) scale(1.08) rotate(5deg);\n }\n 82% {\n opacity: 1;\n }\n 100% {\n opacity: 0;\n transform: translate(330px, -50%) scale(0.9) rotate(10deg);\n }\n}\n.se-import-mirror-head {\n display: flex;\n align-items: end;\n justify-content: space-between;\n gap: 20px;\n}\n.se-import-mirror-head p {\n margin-top: 14px;\n color: var(--se-muted);\n font-size: 16px;\n}\n.se-import-mirror-head span {\n flex: 0 0 auto;\n color: var(--se-muted);\n font-weight: 700;\n}\n.se-import-progress {\n width: 100%;\n height: 14px;\n margin-top: 28px;\n overflow: hidden;\n border: 0;\n border-radius: 999px;\n background: rgba(255, 64, 118, 0.1);\n}\n.se-import-progress::-webkit-progress-bar {\n background: rgba(255, 64, 118, 0.1);\n}\n.se-import-progress::-webkit-progress-value {\n border-radius: 999px;\n background:\n linear-gradient(\n 90deg,\n #ff4076,\n #e7295f);\n}\n.se-import-progress::-moz-progress-bar {\n border-radius: 999px;\n background:\n linear-gradient(\n 90deg,\n #ff4076,\n #e7295f);\n}\n.se-import-asset-list {\n max-height: 360px;\n margin-top: 22px;\n overflow-y: auto;\n border-top: 1px solid rgba(255, 255, 255, 0.08);\n}\n.se-import-asset-row {\n display: grid;\n grid-template-columns: 32px minmax(0, 1fr) auto auto;\n align-items: center;\n gap: 14px;\n min-height: 42px;\n border-bottom: 1px solid rgba(255, 255, 255, 0.08);\n color: var(--se-muted);\n font-size: 13px;\n}\n.se-import-asset-row__icon {\n width: 24px;\n height: 28px;\n justify-self: end;\n object-fit: contain;\n}\n.se-import-asset-row__url {\n overflow: hidden;\n font-family:\n ui-monospace,\n SFMono-Regular,\n Menlo,\n Monaco,\n Consolas,\n monospace;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.se-import-asset-row__state {\n color: #9b8cff;\n font-weight: 800;\n}\n.se-import-asset-row--done .se-import-asset-row__state,\n.se-import-asset-row--reused .se-import-asset-row__state {\n color: var(--se-green);\n}\n.se-import-asset-row--failed .se-import-asset-row__state,\n.se-import-asset-row__error {\n color: #ff8080;\n}\n.se-import-link-button {\n border: 0;\n background: transparent;\n color: #ff9ab6;\n cursor: pointer;\n font: inherit;\n font-weight: 800;\n}\n.se-import-overview {\n margin-top: 28px;\n border: 1px solid var(--se-border);\n border-radius: 12px;\n background: rgba(26, 30, 61, 0.62);\n padding: 28px;\n}\n.se-import-overview h3 {\n margin-bottom: 18px;\n font-size: 18px;\n font-weight: 800;\n}\n.se-import-row {\n display: grid;\n grid-template-columns: 190px minmax(0, 1fr) auto;\n gap: 18px;\n align-items: center;\n min-height: 48px;\n border-top: 1px solid rgba(255, 255, 255, 0.08);\n color: var(--se-muted);\n}\n.se-import-row strong {\n min-width: 0;\n overflow: hidden;\n color: var(--se-text);\n font-weight: 600;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.se-import-row em {\n color: var(--se-muted);\n font-style: normal;\n}\n.se-import-review-card {\n display: flex;\n justify-content: space-between;\n gap: 18px;\n border: 1px solid var(--se-border);\n border-radius: 10px;\n background: rgba(30, 34, 71, 0.8);\n padding: 20px;\n}\n.se-import-review-card__type {\n font-family:\n ui-monospace,\n SFMono-Regular,\n Menlo,\n Monaco,\n Consolas,\n monospace;\n font-weight: 800;\n}\n.se-import-review-card p {\n margin-top: 8px;\n color: var(--se-muted);\n line-height: 1.45;\n}\n.se-import-review-card__status {\n flex: 0 0 auto;\n color: #facc15;\n font-size: 11px;\n font-weight: 800;\n letter-spacing: 0.04em;\n text-transform: uppercase;\n}\n.se-import-review-bulk,\n.se-import-actions--inline {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: flex-end;\n gap: 10px;\n margin-top: 16px;\n}\n.se-import-muted {\n margin-top: 16px;\n color: var(--se-muted);\n font-size: 13px;\n}\n.se-import-generated {\n display: grid;\n gap: 12px;\n margin-top: 18px;\n}\n.se-import-generated__title {\n font-weight: 800;\n}\n.se-import-preview {\n width: 100%;\n height: 260px;\n border: 1px solid var(--se-border);\n border-radius: 8px;\n background: repeating-conic-gradient(#1a1c20 0% 25%, #1d1f24 0% 50%) 50% / 20px 20px;\n}\n.se-import-code {\n margin-top: 16px;\n}\n.se-import-code summary {\n cursor: pointer;\n color: var(--se-muted);\n font-size: 13px;\n}\n.se-import-code pre {\n max-height: 220px;\n margin: 10px 0 0;\n overflow: auto;\n border-radius: 8px;\n background: rgba(0, 0, 0, 0.28);\n padding: 12px;\n color: #d8dcff;\n font-size: 12px;\n}\n.se-import-error {\n margin-top: 18px;\n border: 1px solid rgba(239, 68, 68, 0.45);\n border-radius: 8px;\n background: rgba(220, 38, 38, 0.12);\n padding: 12px;\n color: #fca5a5;\n}\n.se-import-error__title {\n font-weight: 800;\n}\n.se-import-error__hint {\n margin-top: 6px;\n white-space: pre-wrap;\n}\n.se-import-actions {\n display: flex;\n justify-content: flex-end;\n gap: 12px;\n margin-top: 8px;\n}\n@media (max-width: 900px) {\n .se-import__shell {\n min-height: 0;\n padding: 24px 18px;\n }\n .se-import__content {\n min-height: 0;\n padding-top: 32px;\n }\n .se-import-stepper {\n grid-template-columns: repeat(auto-fit, minmax(70px, 1fr));\n }\n .se-import-step__label {\n font-size: 13px;\n }\n .se-import-grid--four,\n .se-import-grid--two,\n .se-import-transfer {\n grid-template-columns: 1fr;\n }\n .se-import-transfer {\n justify-items: center;\n }\n .se-import-transfer__files {\n width: min(360px, 100%);\n }\n .se-import-option,\n .se-import-row,\n .se-import-asset-row {\n grid-template-columns: 1fr;\n }\n .se-import-mirror-head,\n .se-import-review-card {\n align-items: flex-start;\n flex-direction: column;\n }\n}\n');
4767
5096
 
5097
+ // src/assets/source_se.svg
5098
+ var source_se_default = 'data:image/svg+xml,<svg width="157" height="164" viewBox="0 0 157 164" fill="none" xmlns="http://www.w3.org/2000/svg">%0A<rect x="8.5" y="9.5" width="139" height="145" fill="%23242444" stroke="%23FF4076" stroke-width="3"/>%0A<path d="M87.1229 44.9224L89.0788 48.7235C102.041 53.3686 110.947 65.8756 110.947 79.7241C110.947 97.8858 96.1663 112.658 77.9929 112.658C59.8196 112.658 45.0457 97.8858 45.0457 79.7241C45.0457 65.8684 53.9519 53.3686 66.9143 48.7235L68.8702 44.9296C53.1725 49.0337 42 63.3222 42 79.7241C42 99.5592 58.1524 115.701 78.0001 115.701C97.8479 115.701 114 99.5592 114 79.7241C114 63.3222 102.828 49.0265 87.1229 44.9224Z" fill="url(%23paint0_linear_1424_495)"/>%0A<mask id="mask0_1424_495" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="47" y="48" width="62" height="63">%0A<path d="M77.9986 110.386C94.991 110.386 108.766 96.6196 108.766 79.638C108.766 62.6563 94.991 48.8899 77.9986 48.8899C61.0061 48.8899 47.231 62.6563 47.231 79.638C47.231 96.6196 61.0061 110.386 77.9986 110.386Z" fill="white"/>%0A</mask>%0A<g mask="url(%23mask0_1424_495)">%0A<path d="M108.825 78.9093C108.651 81.2463 105.036 90.9619 100.59 90.9619C98.7853 90.9619 98.3812 88.322 98.0853 86.3602C97.2841 89.4905 91.7629 96.2563 87.6706 96.2563C80.2295 96.2563 78.4974 80.5683 77.9921 78.4766C77.4869 80.561 75.7548 96.2563 68.3137 96.2563C64.2215 96.2563 58.7002 89.4977 57.8991 86.3602C57.6031 88.322 57.2062 90.9619 55.3947 90.9619C50.9487 90.9619 47.3328 81.2463 47.1596 78.9093C46.207 80.3158 46.2214 85.9995 47.636 89.6419C52.3561 101.795 64.1782 110.429 77.9849 110.429C91.7917 110.429 103.614 101.795 108.334 89.6419C109.749 85.9995 109.763 80.3231 108.81 78.9093H108.825Z" fill="url(%23paint1_linear_1424_495)"/>%0A<path d="M77.9988 96.2998C79.2258 98.5497 81.8961 101.918 86.4719 101.918C91.0478 101.918 94.6059 97.2014 95.7968 95.2105C95.4143 96.6097 94.642 99.6032 97.132 99.6032C102.841 99.6032 106.969 90.5798 106.969 90.5798C102.466 102.178 91.1777 110.422 77.9988 110.422C64.8199 110.422 53.532 102.178 49.0283 90.5798C49.0283 90.5798 53.1567 99.6032 58.8656 99.6032C61.3628 99.6032 60.5833 96.6021 60.2008 95.2105C61.3917 97.2014 64.8343 101.918 69.5257 101.918C74.1014 101.918 76.7646 98.5428 77.9988 96.2998Z" fill="url(%23paint2_linear_1424_495)"/>%0A<path d="M85.2523 106.419C82.4448 106.419 80.2579 105.546 77.9989 104.14C75.7399 105.539 73.553 106.419 70.7455 106.419C66.4656 106.419 65.1664 105.157 63.6147 104.003C63.658 105.474 65.1592 107.552 66.7109 108.301C70.1537 109.817 74.0221 110.422 77.9989 110.422C81.9756 110.422 85.8442 109.809 89.2868 108.301C90.8385 107.544 92.3326 105.474 92.3831 104.003C90.8313 105.157 89.5323 106.419 85.2523 106.419Z" fill="url(%23paint3_linear_1424_495)"/>%0A</g>%0A<path d="M73.8428 64.7073L69.4257 74.0622C69.2453 74.4446 69.1804 74.8773 69.2525 75.3029L70.6527 83.7562C70.696 84.0303 71.1002 84.0303 71.1435 83.7562L71.9013 79.1473C72.2116 77.2359 73.2292 75.5048 74.7522 74.3003L73.8428 64.7001V64.7073Z" fill="%23FF4800"/>%0A<path d="M82.1584 64.7073L86.5755 74.0622C86.7559 74.4446 86.8208 74.8773 86.7487 75.3029L85.3485 83.7634C85.3052 84.0376 84.9082 84.0376 84.8577 83.7634L84.0999 79.1473C83.7896 77.2359 82.7719 75.5048 81.249 74.3003L82.1584 64.7001V64.7073Z" fill="%23FF4800"/>%0A<path d="M84.5241 50.0291L78.4687 38.2867C78.2739 37.9044 77.7326 37.9044 77.5305 38.2867L71.4751 50.0219C71.2226 50.5052 71.1287 51.0606 71.2081 51.5943L74.261 73.0451L73.8063 75.0503L75.4158 79.4645C75.4519 79.5655 75.5457 79.6304 75.6468 79.6304H80.3308C80.4319 79.6304 80.5257 79.5655 80.5618 79.4645L82.1712 75.0503L81.7165 73.0451L84.7695 51.5943C84.8489 51.0534 84.7551 50.5052 84.5024 50.0219L84.5241 50.0291Z" fill="url(%23paint4_linear_1424_495)"/>%0A<path d="M77.7905 69.5107L75.6397 74.8265C75.3077 75.6488 75.2427 76.5576 75.4593 77.4159L77.7544 86.605C77.8193 86.8575 78.1802 86.8575 78.2379 86.605L80.5331 77.4159C80.7495 76.5576 80.6846 75.6488 80.3526 74.8265L78.2019 69.5107C78.1225 69.3232 77.8554 69.3232 77.7832 69.5107H77.7905Z" fill="%23FF4800"/>%0A<rect width="15" height="16" fill="%23FF4076"/>%0A<rect y="148" width="15" height="16" fill="%23FF4076"/>%0A<rect x="142" width="15" height="16" fill="%23FF4076"/>%0A<rect x="142" y="148" width="15" height="16" fill="%23FF4076"/>%0A<defs>%0A<linearGradient id="paint0_linear_1424_495" x1="78.0001" y1="24.4742" x2="78.0001" y2="138.609" gradientUnits="userSpaceOnUse">%0A<stop offset="0.3" stop-color="%2300ADFF"/>%0A<stop offset="1" stop-color="%231542FF"/>%0A</linearGradient>%0A<linearGradient id="paint1_linear_1424_495" x1="77.9993" y1="79.7965" x2="77.9993" y2="120.816" gradientUnits="userSpaceOnUse">%0A<stop stop-color="%23FCE3AD"/>%0A<stop offset="0.07" stop-color="%23FCE2A4"/>%0A<stop offset="0.19" stop-color="%23FCE08B"/>%0A<stop offset="0.33" stop-color="%23FCDD64"/>%0A<stop offset="0.48" stop-color="%23FCDA32"/>%0A<stop offset="0.66" stop-color="%23F9BC22"/>%0A<stop offset="1" stop-color="%23F57700"/>%0A</linearGradient>%0A<linearGradient id="paint2_linear_1424_495" x1="77.9988" y1="90.2913" x2="77.9988" y2="113.603" gradientUnits="userSpaceOnUse">%0A<stop stop-color="%23FCD619"/>%0A<stop offset="0.35" stop-color="%23F9B811"/>%0A<stop offset="1" stop-color="%23F57700"/>%0A</linearGradient>%0A<linearGradient id="paint3_linear_1424_495" x1="77.9989" y1="101.947" x2="77.9989" y2="112.694" gradientUnits="userSpaceOnUse">%0A<stop stop-color="%23F57700"/>%0A<stop offset="1" stop-color="%23FF4800"/>%0A</linearGradient>%0A<linearGradient id="paint4_linear_1424_495" x1="77.9996" y1="38.0054" x2="77.9996" y2="92.2167" gradientUnits="userSpaceOnUse">%0A<stop stop-color="%23E4E4E4"/>%0A<stop offset="0.88" stop-color="%23B6B8B8"/>%0A</linearGradient>%0A</defs>%0A</svg>%0A';
5099
+
5100
+ // src/assets/source_lumia.svg
5101
+ var source_lumia_default = 'data:image/svg+xml,<svg width="157" height="164" viewBox="0 0 157 164" fill="none" xmlns="http://www.w3.org/2000/svg">%0A<rect x="8.5" y="9.5" width="139" height="145" fill="%23242444" stroke="%23FF4076" stroke-width="3"/>%0A<path d="M81.877 128.851C81.877 131.843 79.5456 132.572 76.6689 132.572C73.7923 132.572 71.4609 131.843 71.4609 128.851H81.877Z" fill="%23FF4076"/>%0A<path d="M77.3916 32C84.8793 32 92.6433 34.2919 98.5977 39.2305C104.635 44.2381 108.604 51.8052 108.604 61.8105C108.603 73.1268 103.496 81.0627 98.6982 88.5176C98.5049 88.8177 98.3117 89.1179 98.1201 89.417C93.1275 97.1931 88.4496 104.881 88.0957 116.606C88.0422 118.373 86.5954 119.776 84.8281 119.776H73.832L74.0488 120.9H82.7236C84.5288 120.901 85.9921 122.364 85.9922 124.169C85.9922 125.975 84.5288 127.438 82.7236 127.438H71.3496C69.7828 127.438 68.4363 126.326 68.1396 124.788L66.6631 117.126C66.4786 116.168 66.7307 115.178 67.3516 114.426C67.9726 113.674 68.8977 113.238 69.873 113.238H81.7549C82.8573 101.346 87.9524 93.1519 92.6182 85.8848C92.7267 85.7157 92.8346 85.5466 92.9424 85.3789C97.9058 77.6522 102.065 71.1771 102.065 61.8105C102.065 53.7021 98.9254 47.9983 94.4229 44.2637C89.8371 40.46 83.6294 38.5381 77.3916 38.5381C65.8229 38.5382 53.5382 46.8207 53.5381 62.1387C53.5381 70.5737 57.3337 78.2378 61.7773 85.8975C62.3932 86.9591 63.0286 88.0311 63.665 89.1064C65.2574 91.7971 66.8614 94.5084 68.2139 97.1221C68.5532 97.7775 68.8826 98.4382 69.1982 99.1035C69.7167 98.1764 70.2716 97.2596 70.8477 96.3564C73.0295 92.9368 75.7271 89.4027 78.3623 85.9502C78.789 85.3914 79.2138 84.8343 79.6348 84.2803C82.7122 80.2309 85.6068 76.2986 87.7383 72.4648C89.8803 68.6123 91.0742 65.165 91.0742 62.084C91.0742 58.1367 89.6207 55.1129 87.3291 53.0479C85.0029 50.9516 81.5869 49.6475 77.3916 49.6475C70.0529 49.6475 64.5294 55.5289 64.5293 62.084C64.5293 64.4095 65.0263 68.0042 66.2227 70.8975C67.4926 73.9687 68.9334 74.959 70.2285 74.959C71.187 74.9589 71.7005 74.6624 72.085 74.2744C72.5587 73.7963 73.0139 72.9773 73.3662 71.7422C74.0688 69.2783 74.0688 66.3016 74.0684 64.2012V64.0537C74.0686 62.2485 75.5317 60.7852 77.3369 60.7852C79.1422 60.7852 80.6062 62.2485 80.6064 64.0537V65.2021C80.5968 67.3108 80.4956 70.5813 79.6533 73.5352C79.1375 75.3439 78.2704 77.3208 76.7295 78.876C75.0993 80.5211 72.9061 81.497 70.2285 81.4971C64.7429 81.4971 61.6996 77.0691 60.1807 73.3955C58.5882 69.5443 57.9912 65.0667 57.9912 62.084C57.9913 51.8935 66.4665 43.1094 77.3916 43.1094C82.9298 43.1094 87.9897 44.8418 91.7061 48.1904C95.4567 51.5704 97.6123 56.3999 97.6123 62.084C97.6123 66.7738 95.8263 71.3721 93.4521 75.6426C91.0671 79.9317 87.9082 84.2005 84.8408 88.2363C84.4031 88.8123 83.9677 89.3835 83.5361 89.9492C80.8874 93.4208 78.3814 96.705 76.3604 99.873C73.9874 103.593 72.5611 106.741 72.2754 109.389C72.0981 111.031 70.7238 112.283 69.0723 112.307C67.4206 112.33 66.0104 111.118 65.7861 109.481C65.3962 106.635 64.1894 103.57 62.4072 100.126C61.1449 97.6868 59.6908 95.2302 58.1406 92.6113C57.4828 91.4997 56.8073 90.3588 56.1221 89.1777C51.6525 81.4736 47 72.474 47 62.1387C47.0001 42.4332 63.0409 32.0001 77.3916 32Z" fill="%23FF4076"/>%0A<path d="M71.6338 65.5146C72.1863 65.262 72.839 65.5052 73.0918 66.0576C73.3445 66.6102 73.1013 67.2627 72.5488 67.5156L68.7217 69.2666C68.1692 69.5191 67.5155 69.2768 67.2627 68.7246C67.0099 68.1719 67.2531 67.5186 67.8057 67.2656L71.6338 65.5146Z" fill="%23FF4076"/>%0A<path d="M82.208 65.5684C82.4316 65.0036 83.0708 64.7267 83.6357 64.9502L87.5498 66.5C88.1146 66.7236 88.3914 67.3628 88.168 67.9277C87.9445 68.4926 87.3052 68.7691 86.7402 68.5459L82.8262 66.9961C82.2611 66.7726 81.9845 66.1333 82.208 65.5684Z" fill="%23FF4076"/>%0A<path d="M66.9834 60.3623C67.1772 59.7865 67.8011 59.4771 68.377 59.6709L72.3662 61.0127C72.942 61.2065 73.2522 61.8304 73.0586 62.4062C72.8649 62.982 72.2408 63.2921 71.665 63.0986L67.6758 61.7559C67.0999 61.5621 66.7896 60.9382 66.9834 60.3623Z" fill="%23FF4076"/>%0A<path d="M86.4482 58.8301C86.9968 58.5686 87.6533 58.8014 87.915 59.3496C88.1767 59.8979 87.9437 60.5547 87.3955 60.8164L83.5977 62.6299C83.049 62.8917 82.3923 62.6587 82.1309 62.1104C81.8692 61.562 82.1012 60.9053 82.6494 60.6436L86.4482 58.8301Z" fill="%23FF4076"/>%0A<path d="M73.084 53.8389C73.6539 53.6282 74.2874 53.9193 74.498 54.4893L75.958 58.4395C76.1686 59.0093 75.8775 59.6428 75.3076 59.8535C74.7377 60.0642 74.1042 59.7721 73.8936 59.2021L72.4336 55.252C72.2231 54.6822 72.5143 54.0496 73.084 53.8389Z" fill="%23FF4076"/>%0A<path d="M80.2275 54.2715C80.4366 53.701 81.0691 53.4072 81.6396 53.6162C82.2102 53.8253 82.5031 54.4578 82.2939 55.0283L80.8447 58.9824C80.6357 59.5529 80.0041 59.8458 79.4336 59.6367C78.8631 59.4277 78.5702 58.7961 78.7793 58.2256L80.2275 54.2715Z" fill="%23FF4076"/>%0A<rect width="15" height="16" fill="%23FF4076"/>%0A<rect y="148" width="15" height="16" fill="%23FF4076"/>%0A<rect x="142" width="15" height="16" fill="%23FF4076"/>%0A<rect x="142" y="148" width="15" height="16" fill="%23FF4076"/>%0A</svg>%0A';
5102
+
5103
+ // src/assets/audio_file.svg
5104
+ var audio_file_default = 'data:image/svg+xml,<svg width="23" height="29" viewBox="0 0 23 29" fill="none" xmlns="http://www.w3.org/2000/svg">%0A<path fill-rule="evenodd" clip-rule="evenodd" d="M22.7675 8.5178V25.5534C22.7675 26.3064 22.4678 27.0287 21.9341 27.5611C21.4004 28.0936 20.6765 28.3925 19.9217 28.3926H2.84579C2.09106 28.3925 1.36706 28.0936 0.833383 27.5611C0.299704 27.0287 0 26.3064 0 25.5534V2.83914C3.46516e-05 2.08616 0.299694 1.36386 0.833383 0.831425C1.36706 0.298996 2.09106 4.12048e-05 2.84579 0H14.2297L22.7675 8.5178ZM15.5614 14.0434C15.4739 14.0241 15.383 14.0247 15.2956 14.045L9.28308 15.4475C9.1509 15.4784 9.03315 15.5534 8.94878 15.6597C8.86444 15.766 8.81842 15.8978 8.81843 16.0335V21.3474C8.45759 21.2198 8.06549 21.2104 7.6989 21.3203C7.33226 21.4304 7.01003 21.6543 6.77901 21.9596C6.54799 22.2648 6.42011 22.6357 6.4138 23.0184C6.4075 23.4012 6.52298 23.776 6.74378 24.0886C6.96461 24.4013 7.27932 24.6355 7.64215 24.7576C8.00495 24.8797 8.39716 24.8834 8.76206 24.7678C9.12698 24.6522 9.44546 24.4232 9.67178 24.1145C9.8981 23.8057 10.0205 23.433 10.0209 23.0501V18.9157L14.831 17.7934V20.1445C14.47 20.0168 14.0778 20.0074 13.7111 20.1174C13.3444 20.2275 13.0222 20.4514 12.7912 20.7567C12.5603 21.0619 12.4326 21.4328 12.4264 21.8155C12.4201 22.1983 12.5358 22.5734 12.7567 22.8861C12.9777 23.1987 13.2923 23.4328 13.6551 23.5547C14.018 23.6766 14.4105 23.6799 14.7754 23.5641C15.1403 23.4483 15.459 23.2193 15.6851 22.9104C15.9112 22.6015 16.0333 22.2288 16.0335 21.8461C16.0335 21.8032 16.032 21.7605 16.0292 21.7184C16.032 21.694 16.0335 21.6697 16.0335 21.6456V14.6306C16.0335 14.5401 16.013 14.4506 15.9736 14.3691C15.9343 14.2876 15.8769 14.216 15.8061 14.1597L15.8022 14.1569C15.7315 14.1017 15.649 14.0628 15.5614 14.0434ZM12.8068 9.93757H20.633L12.8068 2.12945V9.93757Z" fill="white"/>%0A</svg>%0A';
5105
+
5106
+ // src/assets/img_file.svg
5107
+ var img_file_default = 'data:image/svg+xml,<svg width="23" height="29" viewBox="0 0 23 29" fill="none" xmlns="http://www.w3.org/2000/svg">%0A<path fill-rule="evenodd" clip-rule="evenodd" d="M11.7445 13.2276C13.0784 13.2276 14.0388 13.2283 14.7699 13.3267C15.4894 13.4235 15.9266 13.6082 16.2504 13.9322C16.5743 14.2565 16.7591 14.694 16.8559 15.4135C16.9544 16.1445 16.9554 17.1047 16.9554 18.4385V19.8415C16.9554 21.1754 16.9547 22.1358 16.8563 22.8669C16.7595 23.5864 16.5748 24.0236 16.2508 24.3474C15.9265 24.6713 15.489 24.8561 14.7695 24.9529C14.0385 25.0514 13.0784 25.0524 11.7445 25.0524H10.3415C9.00759 25.0524 8.04722 25.0517 7.31607 24.9533C6.59659 24.8565 6.1594 24.6718 5.83564 24.3478C5.51168 24.0235 5.32694 23.586 5.23007 22.8665C5.13166 22.1355 5.13065 21.1754 5.13065 19.8415V18.4385C5.13065 17.1046 5.13135 16.1442 5.22968 15.4131C5.32646 14.6936 5.51124 14.2564 5.83524 13.9326C6.15948 13.6087 6.597 13.4239 7.31646 13.3271C8.04751 13.2287 9.00765 13.2276 10.3415 13.2276H11.7445ZM8.28842 17.3961C7.82545 17.4347 7.44577 17.667 7.10626 17.9398C6.82149 18.1692 6.50307 18.4799 6.13274 18.8496V19.8415C6.13274 20.2034 6.13329 20.5335 6.1347 20.8314L6.91641 20.0497C7.40027 19.5659 7.72283 19.2453 7.98583 19.0328C8.24538 18.8237 8.35792 18.7979 8.40703 18.7944C8.53557 18.7835 8.66468 18.8079 8.78007 18.8656C8.82286 18.8874 8.91773 18.9527 9.08187 19.2445C9.2474 19.5391 9.42752 19.9572 9.69683 20.5856L9.73402 20.6729L9.74615 20.6995C9.85061 20.9435 9.94729 21.1693 10.044 21.3474C10.1458 21.5332 10.2932 21.7566 10.5416 21.9103C10.8341 22.0915 11.1831 22.1586 11.5221 22.0993C11.8097 22.0481 12.0292 21.8962 12.1927 21.7615C12.3484 21.6324 12.5226 21.4582 12.7106 21.2702L12.7313 21.2495C13.0294 20.9514 13.2231 20.7583 13.3823 20.6244C13.5351 20.4954 13.6068 20.4645 13.6461 20.4525C13.7783 20.4126 13.9194 20.4126 14.0516 20.4525C14.0909 20.4645 14.1626 20.4954 14.3155 20.6244C14.4747 20.7583 14.6682 20.9512 14.9668 21.2499L15.9141 22.1972C15.9464 21.6935 15.9519 21.0675 15.9533 20.2517L15.9392 20.238C15.6656 19.9645 15.4301 19.7285 15.2197 19.551C14.9973 19.3637 14.7552 19.1989 14.4564 19.1091C14.0602 18.9896 13.6375 18.9896 13.2413 19.1091C12.9425 19.1996 12.7011 19.3637 12.478 19.551C12.2676 19.7285 12.0321 19.9649 11.7593 20.2384L11.739 20.258C11.5237 20.4726 11.3971 20.5989 11.2982 20.6796L11.2841 20.6917L11.2759 20.6756C11.2149 20.5634 11.1434 20.3997 11.0234 20.1198L10.9862 20.0329L10.9706 19.9965C10.7209 19.4143 10.5113 18.9245 10.3044 18.5556C10.0904 18.1761 9.82392 17.819 9.40873 17.6114C9.06229 17.4379 8.67451 17.3634 8.28842 17.3961ZM14.1996 14.9312C13.6185 14.9312 13.1474 15.4023 13.1474 15.9834C13.1474 16.5645 13.6185 17.0356 14.1996 17.0356C14.7807 17.0356 15.2518 16.5645 15.2518 15.9834C15.2518 15.4023 14.7807 14.9312 14.1996 14.9312Z" fill="white"/>%0A<path fill-rule="evenodd" clip-rule="evenodd" d="M22.7675 8.5178V25.5534C22.7675 26.3064 22.4678 27.0287 21.9341 27.5611C21.4004 28.0936 20.6765 28.3925 19.9217 28.3926H2.84579C2.09106 28.3925 1.36706 28.0936 0.833383 27.5611C0.299704 27.0287 0 26.3064 0 25.5534V2.83914C3.46516e-05 2.08616 0.299694 1.36386 0.833383 0.831425C1.36706 0.298996 2.09106 4.12048e-05 2.84579 0H14.2297L22.7675 8.5178ZM10.3415 12.426C9.03021 12.426 8.00652 12.4252 7.2096 12.5324C6.40115 12.6413 5.76631 12.868 5.26843 13.3654C4.77051 13.863 4.54419 14.4978 4.43544 15.3062C4.32825 16.1032 4.32897 17.1273 4.32897 18.4385V19.8415C4.32897 21.1528 4.32816 22.1765 4.43544 22.9734C4.54428 23.7819 4.77099 24.4167 5.26843 24.9146C5.766 25.4125 6.40083 25.6388 7.20921 25.7476C8.00621 25.8548 9.03026 25.854 10.3415 25.854H11.7445C13.0558 25.854 14.0795 25.8549 14.8764 25.7476C15.6849 25.6387 16.3197 25.412 16.8176 24.9146C17.3155 24.417 17.5418 23.7822 17.6506 22.9738C17.7578 22.1768 17.757 21.1528 17.757 19.8415V18.4385C17.757 17.1272 17.7578 16.1035 17.6506 15.3066C17.5417 14.4982 17.315 13.8633 16.8176 13.3654C16.32 12.8675 15.6852 12.6412 14.8768 12.5324C14.0798 12.4253 13.0557 12.426 11.7445 12.426H10.3415ZM12.8068 9.93757H20.633L12.8068 2.12945V9.93757Z" fill="white"/>%0A</svg>%0A';
5108
+
5109
+ // src/assets/video_file.svg
5110
+ var video_file_default = 'data:image/svg+xml,<svg width="23" height="29" viewBox="0 0 23 29" fill="none" xmlns="http://www.w3.org/2000/svg">%0A<path fill-rule="evenodd" clip-rule="evenodd" d="M22.7675 8.5178V25.5534C22.7675 26.3064 22.4678 27.0287 21.9341 27.5611C21.4004 28.0936 20.6765 28.3925 19.9217 28.3926H2.84579C2.09106 28.3925 1.36706 28.0936 0.833383 27.5611C0.299704 27.0287 0 26.3064 0 25.5534V2.83914C3.46516e-05 2.08616 0.299694 1.36386 0.833383 0.831425C1.36706 0.298996 2.09106 4.12048e-05 2.84579 0H14.2297L22.7675 8.5178ZM7.89972 14.6224C7.44912 14.6224 7.01703 14.8013 6.69838 15.1199C6.37968 15.4386 6.20046 15.8709 6.20046 16.3216V20.9563C6.20046 21.407 6.37968 21.8393 6.69838 22.158C7.01703 22.4766 7.44912 22.6555 7.89972 22.6556H13.1525C13.6032 22.6556 14.0355 22.4767 14.3542 22.158C14.6729 21.8393 14.8517 21.407 14.8517 20.9563V19.9714L16.9648 21.8531C17.4622 22.296 18.2506 21.9425 18.2506 21.2757V15.7748C18.2506 15.1074 17.4622 14.7545 16.9648 15.1974L14.8517 17.0791V16.3216C14.8517 15.8709 14.6729 15.4386 14.3542 15.1199C14.0355 14.8012 13.6032 14.6224 13.1525 14.6224H7.89972ZM12.8068 9.93757H20.633L12.8068 2.12945V9.93757Z" fill="white"/>%0A</svg>%0A';
5111
+
5112
+ // src/assets/sound.svg
5113
+ var sound_default = 'data:image/svg+xml,<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">%0A<path d="M18.622 3.21699C18.7399 3.31063 18.8351 3.42968 18.9005 3.56526C18.966 3.70084 19 3.84944 19 3.99999V15.667C19 15.707 18.9977 15.7473 18.993 15.788C18.9977 15.858 19 15.9287 19 16C18.9997 16.6368 18.7967 17.2569 18.4206 17.7707C18.0444 18.2844 17.5145 18.6652 16.9076 18.8578C16.3007 19.0505 15.6482 19.045 15.0446 18.8422C14.441 18.6394 13.9176 18.2498 13.5501 17.7298C13.1826 17.2098 12.9901 16.5864 13.0004 15.9497C13.0107 15.313 13.2234 14.6961 13.6076 14.1883C13.9918 13.6805 14.5276 13.3082 15.1375 13.1251C15.7473 12.942 16.3997 12.9577 17 13.17V9.25999L9 11.127V18.003C8.99924 18.6397 8.79595 19.2596 8.41953 19.7731C8.04312 20.2866 7.5131 20.667 6.90616 20.8593C6.29923 21.0516 5.64684 21.0458 5.0434 20.8428C4.43996 20.6398 3.91676 20.2501 3.54948 19.73C3.1822 19.2099 2.98991 18.5865 3.00041 17.9499C3.01091 17.3133 3.22367 16.6966 3.60789 16.1889C3.99212 15.6813 4.5279 15.309 5.13771 15.126C5.74752 14.943 6.39973 14.9588 7 15.171V6.33299C6.99997 6.10722 7.07635 5.88808 7.2167 5.71124C7.35705 5.53439 7.55312 5.41024 7.773 5.35899L17.773 3.02599C17.9183 2.99221 18.0694 2.99139 18.2151 3.02357C18.3608 3.05576 18.4974 3.12013 18.615 3.21199L18.622 3.21699Z" fill="white"/>%0A</svg>%0A';
5114
+
5115
+ // src/assets/img.svg
5116
+ var img_default = 'data:image/svg+xml,<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">%0A<path fill-rule="evenodd" clip-rule="evenodd" d="M3.172 3.17199C2 4.34299 2 6.22899 2 9.99999V14C2 17.771 2 19.657 3.172 20.828C4.344 21.999 6.229 22 10 22H14C17.771 22 19.657 22 20.828 20.828C21.999 19.656 22 17.771 22 14V9.99999C22 7.15999 22 5.38999 21.5 4.18899V17C21.0246 17.0001 20.5538 16.9065 20.1146 16.7246C19.6753 16.5427 19.2762 16.2761 18.94 15.94L18.188 15.188C17.466 14.466 17.106 14.106 16.697 13.954C16.2474 13.7868 15.7526 13.7868 15.303 13.954C14.894 14.106 14.533 14.466 13.813 15.188L13.699 15.301C13.114 15.886 12.821 16.179 12.51 16.233C12.2685 16.2756 12.0197 16.2279 11.811 16.099C11.543 15.933 11.38 15.552 11.053 14.791L11 14.667C10.25 12.917 9.876 12.043 9.222 11.715C8.89266 11.5499 8.52411 11.4789 8.157 11.51C7.428 11.572 6.756 12.245 5.41 13.59L3.5 15.5V2.88699C3.384 2.97299 3.27467 3.06799 3.172 3.17199Z" fill="white"/>%0A<mask id="mask0_1424_377" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="2" y="2" width="20" height="20">%0A<path d="M2 10C2 6.229 2 4.343 3.172 3.172C4.344 2.001 6.229 2 10 2H14C17.771 2 19.657 2 20.828 3.172C21.999 4.344 22 6.229 22 10V14C22 17.771 22 19.657 20.828 20.828C19.656 21.999 17.771 22 14 22H10C6.229 22 4.343 22 3.172 20.828C2.001 19.656 2 17.771 2 14V10Z" fill="white"/>%0A</mask>%0A<g mask="url(%23mask0_1424_377)">%0A<path d="M2 10C2 6.229 2 4.343 3.172 3.172C4.344 2.001 6.229 2 10 2H14C17.771 2 19.657 2 20.828 3.172C21.999 4.344 22 6.229 22 10V14C22 17.771 22 19.657 20.828 20.828C19.656 21.999 17.771 22 14 22H10C6.229 22 4.343 22 3.172 20.828C2.001 19.656 2 17.771 2 14V10Z" stroke="white" stroke-width="4"/>%0A</g>%0A<path d="M15 11C16.1046 11 17 10.1046 17 9C17 7.89543 16.1046 7 15 7C13.8954 7 13 7.89543 13 9C13 10.1046 13.8954 11 15 11Z" fill="white"/>%0A</svg>%0A';
5117
+
5118
+ // src/assets/video.svg
5119
+ var video_default = 'data:image/svg+xml,<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">%0A<path d="M5 5.5C4.27065 5.5 3.57118 5.78973 3.05546 6.30546C2.53973 6.82118 2.25 7.52065 2.25 8.25V15.75C2.25 16.4793 2.53973 17.1788 3.05546 17.6945C3.57118 18.2103 4.27065 18.5 5 18.5H13.5C14.2293 18.5 14.9288 18.2103 15.4445 17.6945C15.9603 17.1788 16.25 16.4793 16.25 15.75V14.156L19.669 17.201C20.474 17.918 21.75 17.346 21.75 16.267V7.365C21.75 6.285 20.474 5.714 19.669 6.431L16.25 9.476V8.25C16.25 7.52065 15.9603 6.82118 15.4445 6.30546C14.9288 5.78973 14.2293 5.5 13.5 5.5H5Z" fill="white"/>%0A</svg>%0A';
5120
+
4768
5121
  // src/se-import/ui/SEImportWizard.tsx
4769
- import { Fragment as Fragment3, jsx as jsx13, jsxs as jsxs7 } from "react/jsx-runtime";
4770
- var sourceSEIcon = new URL("../../assets/source_se.svg", import.meta.url).href;
4771
- var sourceLumiaIcon = new URL(
4772
- "../../assets/source_lumia.svg",
4773
- import.meta.url
4774
- ).href;
4775
- var audioFileIcon = new URL("../../assets/audio_file.svg", import.meta.url).href;
4776
- var imageFileIcon = new URL("../../assets/img_file.svg", import.meta.url).href;
4777
- var videoFileIcon = new URL("../../assets/video_file.svg", import.meta.url).href;
4778
- var audioIcon = new URL("../../assets/sound.svg", import.meta.url).href;
4779
- var imageIcon = new URL("../../assets/img.svg", import.meta.url).href;
4780
- var videoIcon = new URL("../../assets/video.svg", import.meta.url).href;
5122
+ import { Fragment as Fragment4, jsx as jsx13, jsxs as jsxs7 } from "react/jsx-runtime";
4781
5123
  var CORE_STEPS = [
4782
5124
  { key: "connect", label: "Connect" },
5125
+ { key: "pick", label: "Overlays" },
4783
5126
  { key: "url", label: "Load" },
4784
5127
  { key: "discovery", label: "Discovery" },
4785
5128
  { key: "options", label: "Options" },
@@ -4932,6 +5275,136 @@ function StepHeader({
4932
5275
  ] })
4933
5276
  ] });
4934
5277
  }
5278
+ var JWT_FEATURES = [
5279
+ "All your overlays \u2014 pick one, many, or all",
5280
+ "Live counters (follower / sub / tip / bits goals)",
5281
+ "Chat bot commands",
5282
+ "Loyalty points + watch time",
5283
+ "Recent activity replay",
5284
+ "Bits leaderboard"
5285
+ ];
5286
+ function ModeCard({
5287
+ primary,
5288
+ title,
5289
+ body,
5290
+ caveat,
5291
+ onClick
5292
+ }) {
5293
+ return /* @__PURE__ */ jsxs7(
5294
+ "button",
5295
+ {
5296
+ type: "button",
5297
+ onClick,
5298
+ style: {
5299
+ display: "flex",
5300
+ flexDirection: "column",
5301
+ alignItems: "flex-start",
5302
+ gap: 14,
5303
+ padding: 24,
5304
+ minHeight: 280,
5305
+ borderRadius: 14,
5306
+ border: primary ? "2px solid var(--se-primary)" : "1px solid var(--se-border)",
5307
+ background: primary ? "rgba(255, 64, 118, 0.08)" : "transparent",
5308
+ color: "var(--se-text)",
5309
+ textAlign: "left",
5310
+ cursor: "pointer",
5311
+ transition: "background 120ms, border-color 120ms, transform 120ms"
5312
+ },
5313
+ onMouseEnter: (e) => {
5314
+ e.currentTarget.style.background = primary ? "rgba(255, 64, 118, 0.14)" : "rgba(255, 255, 255, 0.04)";
5315
+ if (!primary)
5316
+ e.currentTarget.style.borderColor = "var(--se-border-strong)";
5317
+ e.currentTarget.style.transform = "translateY(-2px)";
5318
+ },
5319
+ onMouseLeave: (e) => {
5320
+ e.currentTarget.style.background = primary ? "rgba(255, 64, 118, 0.08)" : "transparent";
5321
+ if (!primary)
5322
+ e.currentTarget.style.borderColor = "var(--se-border)";
5323
+ e.currentTarget.style.transform = "translateY(0)";
5324
+ },
5325
+ children: [
5326
+ /* @__PURE__ */ jsx13(
5327
+ "div",
5328
+ {
5329
+ style: {
5330
+ fontSize: 18,
5331
+ fontWeight: 800,
5332
+ color: primary ? "var(--se-primary)" : "var(--se-text)"
5333
+ },
5334
+ children: title
5335
+ }
5336
+ ),
5337
+ /* @__PURE__ */ jsx13("div", { style: { fontSize: 13.5, lineHeight: 1.55, color: "var(--se-text)", width: "100%" }, children: body }),
5338
+ /* @__PURE__ */ jsx13("div", { style: { fontSize: 12, color: "var(--se-muted)", marginTop: "auto" }, children: caveat })
5339
+ ]
5340
+ }
5341
+ );
5342
+ }
5343
+ function StepModePicker({
5344
+ onChooseJwt,
5345
+ onChooseUrl
5346
+ }) {
5347
+ return /* @__PURE__ */ jsxs7("div", { className: panelClass("se-import-panel--wide"), children: [
5348
+ /* @__PURE__ */ jsx13(
5349
+ StepHeader,
5350
+ {
5351
+ number: 1,
5352
+ title: "What would you like to import?",
5353
+ subtitle: "Pick a starting point. You can change your mind from the Back button on the next step."
5354
+ }
5355
+ ),
5356
+ /* @__PURE__ */ jsxs7(
5357
+ "div",
5358
+ {
5359
+ style: {
5360
+ display: "grid",
5361
+ gridTemplateColumns: "repeat(auto-fit, minmax(300px, 1fr))",
5362
+ gap: 18,
5363
+ marginTop: 8
5364
+ },
5365
+ children: [
5366
+ /* @__PURE__ */ jsx13(
5367
+ ModeCard,
5368
+ {
5369
+ primary: true,
5370
+ title: "Import everything from StreamElements",
5371
+ body: /* @__PURE__ */ jsxs7(Fragment4, { children: [
5372
+ /* @__PURE__ */ jsx13("div", { style: { marginBottom: 10 }, children: "Connect once and bring over your entire SE setup:" }),
5373
+ /* @__PURE__ */ jsx13(
5374
+ "ul",
5375
+ {
5376
+ style: {
5377
+ margin: 0,
5378
+ paddingLeft: 18,
5379
+ display: "grid",
5380
+ gap: 6,
5381
+ fontSize: 13
5382
+ },
5383
+ children: JWT_FEATURES.map((feat) => /* @__PURE__ */ jsx13("li", { children: feat }, feat))
5384
+ }
5385
+ )
5386
+ ] }),
5387
+ caveat: "Requires pasting your StreamElements JWT. Stored only in memory \u2014 never sent to Lumia.",
5388
+ onClick: onChooseJwt
5389
+ }
5390
+ ),
5391
+ /* @__PURE__ */ jsx13(
5392
+ ModeCard,
5393
+ {
5394
+ title: "Import a single overlay",
5395
+ body: /* @__PURE__ */ jsxs7(Fragment4, { children: [
5396
+ "Paste a single overlay's public preview URL to bring just that overlay over.",
5397
+ /* @__PURE__ */ jsx13("div", { style: { marginTop: 10, color: "var(--se-muted)" }, children: "No login required \u2014 perfect for trying one overlay before deciding whether to migrate your whole account." })
5398
+ ] }),
5399
+ caveat: "The URL contains the access token; treat it like a password.",
5400
+ onClick: onChooseUrl
5401
+ }
5402
+ )
5403
+ ]
5404
+ }
5405
+ )
5406
+ ] });
5407
+ }
4935
5408
  function StepConnect({
4936
5409
  jwt,
4937
5410
  setJwt,
@@ -5057,6 +5530,140 @@ function StepConnect({
5057
5530
  error && /* @__PURE__ */ jsx13(ErrorPanel, { message: error.message, hint: error.hint })
5058
5531
  ] });
5059
5532
  }
5533
+ function StepPickOverlay({
5534
+ overlays,
5535
+ loading,
5536
+ error,
5537
+ selectedIds,
5538
+ toggleId,
5539
+ selectAll,
5540
+ clearAll,
5541
+ onRetry
5542
+ }) {
5543
+ const selectedCount = selectedIds.size;
5544
+ const total = overlays?.length ?? 0;
5545
+ const allSelected = total > 0 && selectedCount === total;
5546
+ const hasAnySelected = selectedCount > 0;
5547
+ return /* @__PURE__ */ jsxs7("div", { className: panelClass("se-import-panel--wide"), children: [
5548
+ /* @__PURE__ */ jsx13(
5549
+ StepHeader,
5550
+ {
5551
+ number: 2,
5552
+ title: "Pick the overlays to import",
5553
+ subtitle: `Select one or more StreamElements overlays from your account. We'll run each one through the discovery \u2192 review \u2192 import pipeline in sequence. ${total > 0 ? `${total} overlay${total === 1 ? "" : "s"} on this channel.` : ""}`
5554
+ }
5555
+ ),
5556
+ loading && /* @__PURE__ */ jsxs7("div", { className: "se-import-help se-import-help--info", children: [
5557
+ /* @__PURE__ */ jsx13("div", { className: "se-import-help__icon", children: "\u2026" }),
5558
+ /* @__PURE__ */ jsx13("div", { children: "Fetching your StreamElements overlays." })
5559
+ ] }),
5560
+ error && /* @__PURE__ */ jsxs7(Fragment4, { children: [
5561
+ /* @__PURE__ */ jsx13(ErrorPanel, { message: error.message, hint: error.hint }),
5562
+ /* @__PURE__ */ jsx13("div", { style: { marginTop: 12 }, children: /* @__PURE__ */ jsx13(LSButton, { type: "button", color: "secondary", onClick: onRetry, label: "Retry" }) })
5563
+ ] }),
5564
+ !loading && !error && overlays && overlays.length === 0 && /* @__PURE__ */ jsxs7("div", { className: "se-import-help se-import-help--info", children: [
5565
+ /* @__PURE__ */ jsx13("div", { className: "se-import-help__icon", children: "i" }),
5566
+ /* @__PURE__ */ jsxs7("div", { children: [
5567
+ /* @__PURE__ */ jsx13("div", { className: "se-import-help__title", children: "No overlays found" }),
5568
+ /* @__PURE__ */ jsx13("p", { children: "This StreamElements account doesn't have any overlays yet. Create one in the SE dashboard, or use the legacy preview URL flow from the Connect step." })
5569
+ ] })
5570
+ ] }),
5571
+ !loading && !error && overlays && overlays.length > 0 && /* @__PURE__ */ jsxs7(Fragment4, { children: [
5572
+ /* @__PURE__ */ jsxs7("div", { className: "se-import-actions se-import-actions--inline", style: { marginTop: 24, marginBottom: 16, justifyContent: "space-between", alignItems: "center", display: "flex" }, children: [
5573
+ /* @__PURE__ */ jsxs7("label", { style: { display: "inline-flex", alignItems: "center", gap: 8, cursor: "pointer", fontSize: 14, fontWeight: 600 }, children: [
5574
+ /* @__PURE__ */ jsx13(
5575
+ "input",
5576
+ {
5577
+ type: "checkbox",
5578
+ checked: allSelected,
5579
+ ref: (el) => {
5580
+ if (el) el.indeterminate = !allSelected && hasAnySelected;
5581
+ },
5582
+ onChange: () => allSelected ? clearAll() : selectAll()
5583
+ }
5584
+ ),
5585
+ /* @__PURE__ */ jsxs7("span", { children: [
5586
+ allSelected ? "Deselect all" : "Select all",
5587
+ " (",
5588
+ selectedCount,
5589
+ "/",
5590
+ total,
5591
+ ")"
5592
+ ] })
5593
+ ] }),
5594
+ /* @__PURE__ */ jsx13("span", { style: { fontSize: 13, color: "var(--se-muted)" }, children: hasAnySelected ? `${selectedCount} overlay${selectedCount === 1 ? "" : "s"} queued. Each runs through the full import flow in sequence.` : "Click overlays below to select them." })
5595
+ ] }),
5596
+ /* @__PURE__ */ jsx13(
5597
+ "div",
5598
+ {
5599
+ className: "se-import-grid",
5600
+ style: {
5601
+ gridTemplateColumns: "repeat(auto-fill, minmax(220px, 1fr))",
5602
+ gap: 12,
5603
+ maxHeight: 480,
5604
+ overflowY: "auto",
5605
+ padding: 4
5606
+ },
5607
+ children: overlays.map((o) => {
5608
+ const selected = selectedIds.has(o._id);
5609
+ return /* @__PURE__ */ jsxs7(
5610
+ "label",
5611
+ {
5612
+ style: {
5613
+ position: "relative",
5614
+ display: "flex",
5615
+ flexDirection: "column",
5616
+ border: selected ? "2px solid var(--se-primary)" : "1px solid var(--se-border)",
5617
+ borderRadius: 10,
5618
+ padding: 10,
5619
+ background: selected ? "rgba(255, 64, 118, 0.08)" : "rgba(28, 32, 66, 0.45)",
5620
+ cursor: "pointer",
5621
+ transition: "border-color 120ms, background 120ms"
5622
+ },
5623
+ children: [
5624
+ /* @__PURE__ */ jsx13(
5625
+ "input",
5626
+ {
5627
+ type: "checkbox",
5628
+ checked: selected,
5629
+ onChange: () => toggleId(o._id),
5630
+ style: { position: "absolute", top: 10, right: 10, accentColor: "var(--se-primary)", zIndex: 1 }
5631
+ }
5632
+ ),
5633
+ o.preview ? /* @__PURE__ */ jsx13(
5634
+ "img",
5635
+ {
5636
+ src: o.preview,
5637
+ alt: "",
5638
+ style: {
5639
+ width: "100%",
5640
+ height: 110,
5641
+ objectFit: "cover",
5642
+ borderRadius: 6,
5643
+ background: "var(--se-panel-2)"
5644
+ },
5645
+ loading: "lazy"
5646
+ }
5647
+ ) : /* @__PURE__ */ jsx13("div", { style: { width: "100%", height: 110, borderRadius: 6, background: "var(--se-panel-2)" } }),
5648
+ /* @__PURE__ */ jsx13("div", { style: { marginTop: 8, fontSize: 14, fontWeight: 700, color: "var(--se-text)", paddingRight: 28 }, children: o.name ?? "Unnamed overlay" }),
5649
+ /* @__PURE__ */ jsxs7("div", { style: { marginTop: 4, fontSize: 11, color: "var(--se-muted)" }, children: [
5650
+ o.type ?? "regular",
5651
+ o.settings?.width && o.settings?.height ? ` \xB7 ${o.settings.width}\xD7${o.settings.height}` : ""
5652
+ ] }),
5653
+ o.updatedAt && /* @__PURE__ */ jsxs7("div", { style: { marginTop: 2, fontSize: 11, color: "var(--se-muted)" }, children: [
5654
+ "Updated ",
5655
+ new Date(o.updatedAt).toLocaleDateString()
5656
+ ] })
5657
+ ]
5658
+ },
5659
+ o._id
5660
+ );
5661
+ })
5662
+ }
5663
+ )
5664
+ ] })
5665
+ ] });
5666
+ }
5060
5667
  function StepURL({
5061
5668
  url,
5062
5669
  setUrl,
@@ -5077,7 +5684,7 @@ function StepURL({
5077
5684
  {
5078
5685
  label: "Overlay URL",
5079
5686
  value: url,
5080
- onChange: setUrl,
5687
+ onChange: (_, next) => setUrl(String(next ?? "")),
5081
5688
  placeholder: "https://streamelements.com/overlay/<id>/<token>",
5082
5689
  disabled: busy
5083
5690
  }
@@ -5208,19 +5815,19 @@ function mirrorLabel(state) {
5208
5815
  }
5209
5816
  function fileTypeIcon(url) {
5210
5817
  const ext = filenameFromURL(url).split(".").pop()?.toLowerCase();
5211
- if (ext === "mp3" || ext === "wav" || ext === "ogg") return audioIcon;
5212
- if (ext === "webm" || ext === "mp4" || ext === "mov") return videoIcon;
5213
- return imageIcon;
5818
+ if (ext === "mp3" || ext === "wav" || ext === "ogg") return sound_default;
5819
+ if (ext === "webm" || ext === "mp4" || ext === "mov") return video_default;
5820
+ return img_default;
5214
5821
  }
5215
5822
  function AssetTransferAnimation() {
5216
5823
  return /* @__PURE__ */ jsxs7("div", { className: "se-import-transfer", "aria-hidden": "true", children: [
5217
- /* @__PURE__ */ jsx13("div", { className: "se-import-transfer__source se-import-transfer__source--se", children: /* @__PURE__ */ jsx13("img", { src: sourceSEIcon, alt: "" }) }),
5824
+ /* @__PURE__ */ jsx13("div", { className: "se-import-transfer__source se-import-transfer__source--se", children: /* @__PURE__ */ jsx13("img", { src: source_se_default, alt: "" }) }),
5218
5825
  /* @__PURE__ */ jsxs7("div", { className: "se-import-transfer__files", children: [
5219
5826
  /* @__PURE__ */ jsx13(
5220
5827
  "img",
5221
5828
  {
5222
5829
  className: "se-import-transfer__file se-import-transfer__file--one",
5223
- src: imageFileIcon,
5830
+ src: img_file_default,
5224
5831
  alt: ""
5225
5832
  }
5226
5833
  ),
@@ -5228,7 +5835,7 @@ function AssetTransferAnimation() {
5228
5835
  "img",
5229
5836
  {
5230
5837
  className: "se-import-transfer__file se-import-transfer__file--two",
5231
- src: audioFileIcon,
5838
+ src: audio_file_default,
5232
5839
  alt: ""
5233
5840
  }
5234
5841
  ),
@@ -5236,7 +5843,7 @@ function AssetTransferAnimation() {
5236
5843
  "img",
5237
5844
  {
5238
5845
  className: "se-import-transfer__file se-import-transfer__file--three",
5239
- src: imageFileIcon,
5846
+ src: img_file_default,
5240
5847
  alt: ""
5241
5848
  }
5242
5849
  ),
@@ -5244,12 +5851,12 @@ function AssetTransferAnimation() {
5244
5851
  "img",
5245
5852
  {
5246
5853
  className: "se-import-transfer__file se-import-transfer__file--four",
5247
- src: videoFileIcon,
5854
+ src: video_file_default,
5248
5855
  alt: ""
5249
5856
  }
5250
5857
  )
5251
5858
  ] }),
5252
- /* @__PURE__ */ jsx13("div", { className: "se-import-transfer__source se-import-transfer__source--lumia", children: /* @__PURE__ */ jsx13("img", { src: sourceLumiaIcon, alt: "" }) })
5859
+ /* @__PURE__ */ jsx13("div", { className: "se-import-transfer__source se-import-transfer__source--lumia", children: /* @__PURE__ */ jsx13("img", { src: source_lumia_default, alt: "" }) })
5253
5860
  ] });
5254
5861
  }
5255
5862
  function StepMirror({
@@ -5400,7 +6007,7 @@ function StepReview({
5400
6007
  /* @__PURE__ */ jsx13("summary", { children: "Original StreamElements config" }),
5401
6008
  /* @__PURE__ */ jsx13("pre", { children: JSON.stringify(item.seWidget.variables ?? {}, null, 2) })
5402
6009
  ] }),
5403
- /* @__PURE__ */ jsx13("div", { className: "se-import-actions se-import-actions--inline", children: state === "generated" ? /* @__PURE__ */ jsxs7(Fragment3, { children: [
6010
+ /* @__PURE__ */ jsx13("div", { className: "se-import-actions se-import-actions--inline", children: state === "generated" ? /* @__PURE__ */ jsxs7(Fragment4, { children: [
5404
6011
  /* @__PURE__ */ jsx13(
5405
6012
  LSButton,
5406
6013
  {
@@ -5428,7 +6035,7 @@ function StepReview({
5428
6035
  label: "Use this and continue"
5429
6036
  }
5430
6037
  )
5431
- ] }) : /* @__PURE__ */ jsxs7(Fragment3, { children: [
6038
+ ] }) : /* @__PURE__ */ jsxs7(Fragment4, { children: [
5432
6039
  !item.flaggedOff && /* @__PURE__ */ jsx13(
5433
6040
  LSButton,
5434
6041
  {
@@ -5554,13 +6161,23 @@ function SEImportWizard({
5554
6161
  t
5555
6162
  } = bindings;
5556
6163
  const translate = (key, fallback) => t ? t(key) ?? fallback : fallback;
5557
- const [step, setStep] = useState6("connect");
6164
+ const [step, setStep] = useState6(initialUrl ? "url" : "mode");
6165
+ const [entryMode, setEntryMode] = useState6(
6166
+ initialUrl ? "url" : null
6167
+ );
5558
6168
  const [jwt, setJwt] = useState6("");
5559
6169
  const [seClient, setSeClient] = useState6(null);
5560
6170
  const [channels, setChannels] = useState6(null);
5561
6171
  const [selectedChannelId, setSelectedChannelId] = useState6(null);
5562
6172
  const [connectError, setConnectError] = useState6(null);
5563
6173
  const [connecting, setConnecting] = useState6(false);
6174
+ const [overlayList, setOverlayList] = useState6(null);
6175
+ const [overlaysLoading, setOverlaysLoading] = useState6(false);
6176
+ const [overlaysError, setOverlaysError] = useState6(null);
6177
+ const [selectedOverlayIds, setSelectedOverlayIds] = useState6(/* @__PURE__ */ new Set());
6178
+ const [overlayQueue, setOverlayQueue] = useState6([]);
6179
+ const [currentOverlayIndex, setCurrentOverlayIndex] = useState6(0);
6180
+ const [totalOverlaysToImport, setTotalOverlaysToImport] = useState6(0);
5564
6181
  const [url, setUrl] = useState6(initialUrl);
5565
6182
  const [loadError, setLoadError] = useState6(null);
5566
6183
  const [loading, setLoading] = useState6(false);
@@ -5630,6 +6247,73 @@ function SEImportWizard({
5630
6247
  setConnecting(false);
5631
6248
  }
5632
6249
  }, [bindings, jwt]);
6250
+ const loadOverlayList = useCallback3(async (client) => {
6251
+ setOverlaysError(null);
6252
+ setOverlaysLoading(true);
6253
+ try {
6254
+ const list = await fetchSEOverlays(client);
6255
+ setOverlayList(list);
6256
+ } catch (e) {
6257
+ if (e instanceof SEAuthError) {
6258
+ setOverlaysError({ message: e.message, hint: e.code === "unauthorized" ? "Generate a new JWT on streamelements.com/dashboard/account/channels." : void 0 });
6259
+ bindings.onAuthError?.(e);
6260
+ } else {
6261
+ setOverlaysError({ message: e instanceof Error ? e.message : "Could not load your StreamElements overlays." });
6262
+ }
6263
+ } finally {
6264
+ setOverlaysLoading(false);
6265
+ }
6266
+ }, [bindings]);
6267
+ const toggleOverlaySelection = useCallback3((id) => {
6268
+ setSelectedOverlayIds((prev) => {
6269
+ const next = new Set(prev);
6270
+ if (next.has(id)) next.delete(id);
6271
+ else next.add(id);
6272
+ return next;
6273
+ });
6274
+ }, []);
6275
+ const selectAllOverlays = useCallback3(() => {
6276
+ setSelectedOverlayIds(new Set((overlayList ?? []).map((o) => o._id)));
6277
+ }, [overlayList]);
6278
+ const clearAllOverlays = useCallback3(() => {
6279
+ setSelectedOverlayIds(/* @__PURE__ */ new Set());
6280
+ }, []);
6281
+ const loadBootstrapAndKickoff = useCallback3(async (client, overlayId) => {
6282
+ setLoadError(null);
6283
+ setLoading(true);
6284
+ try {
6285
+ const bootstrap = await fetchSEBootstrapForOverlay(client, overlayId);
6286
+ if (!isSEBootstrap(bootstrap)) {
6287
+ throw new Error("StreamElements returned a payload that doesn't look like an overlay bootstrap.");
6288
+ }
6289
+ const imported = importSEBootstrap(bootstrap);
6290
+ setResult(imported);
6291
+ setOptions((o) => ({ ...o, name: imported.overlay.name }));
6292
+ setOriginalReviewItems(imported.reviewItems);
6293
+ setRowStates(Object.fromEntries(imported.reviewItems.map((r) => [r.moduleId, { state: "pending" }])));
6294
+ setReviewIndex(0);
6295
+ setMirrorRows([]);
6296
+ mirrorStartedRef.current = false;
6297
+ setStep("discovery");
6298
+ } catch (e) {
6299
+ setLoadError({
6300
+ message: "Could not load the overlay.",
6301
+ hint: e?.message ?? String(e)
6302
+ });
6303
+ } finally {
6304
+ setLoading(false);
6305
+ }
6306
+ }, []);
6307
+ const handlePickConfirm = useCallback3(async () => {
6308
+ if (!seClient) return;
6309
+ const ids = Array.from(selectedOverlayIds);
6310
+ if (ids.length === 0) return;
6311
+ const [first, ...rest] = ids;
6312
+ setOverlayQueue(rest);
6313
+ setTotalOverlaysToImport(ids.length);
6314
+ setCurrentOverlayIndex(1);
6315
+ await loadBootstrapAndKickoff(seClient, first);
6316
+ }, [seClient, selectedOverlayIds, loadBootstrapAndKickoff]);
5633
6317
  const handleLoad = useCallback3(async () => {
5634
6318
  setLoadError(null);
5635
6319
  const parts = extractSEPreviewParts(url);
@@ -5765,6 +6449,11 @@ function SEImportWizard({
5765
6449
  void runMirror();
5766
6450
  }
5767
6451
  }, [step, runMirror]);
6452
+ useEffect6(() => {
6453
+ if (step === "pick" && seClient && overlayList === null && !overlaysLoading) {
6454
+ void loadOverlayList(seClient);
6455
+ }
6456
+ }, [step, seClient, overlayList, overlaysLoading, loadOverlayList]);
5768
6457
  const advanceReview = useCallback3(() => {
5769
6458
  if (reviewIndex + 1 < originalReviewItems.length)
5770
6459
  setReviewIndex(reviewIndex + 1);
@@ -5898,7 +6587,23 @@ function SEImportWizard({
5898
6587
  setStep("confirm");
5899
6588
  };
5900
6589
  const handleFinalImport = useCallback3(async () => {
5901
- if (!result) return;
6590
+ if (!result || !seClient) {
6591
+ if (!result) return;
6592
+ setImporting(true);
6593
+ try {
6594
+ const body = { name: options.name.trim() || result.overlay.name, description: result.overlay.description, settings: result.overlay.settings };
6595
+ const { uuid } = await saveOverlay(body);
6596
+ notify.success("Imported overlay from StreamElements.");
6597
+ mirrorAbortRef.current?.abort();
6598
+ onClose();
6599
+ if (uuid) onComplete({ overlayId: uuid });
6600
+ } catch (error) {
6601
+ notify.error(`Import failed: ${extractErrorMessage(error)}`);
6602
+ } finally {
6603
+ setImporting(false);
6604
+ }
6605
+ return;
6606
+ }
5902
6607
  setImporting(true);
5903
6608
  try {
5904
6609
  const body = {
@@ -5907,7 +6612,19 @@ function SEImportWizard({
5907
6612
  settings: result.overlay.settings
5908
6613
  };
5909
6614
  const { uuid } = await saveOverlay(body);
5910
- notify.success("Imported overlay from StreamElements.");
6615
+ const hasMore = overlayQueue.length > 0;
6616
+ if (hasMore) {
6617
+ notify.success(`Imported ${currentOverlayIndex} of ${totalOverlaysToImport} overlays from StreamElements.`);
6618
+ const [nextId, ...rest] = overlayQueue;
6619
+ setOverlayQueue(rest);
6620
+ setCurrentOverlayIndex(currentOverlayIndex + 1);
6621
+ setImporting(false);
6622
+ await loadBootstrapAndKickoff(seClient, nextId);
6623
+ return;
6624
+ }
6625
+ notify.success(
6626
+ totalOverlaysToImport > 1 ? `Imported all ${totalOverlaysToImport} overlays from StreamElements.` : "Imported overlay from StreamElements."
6627
+ );
5911
6628
  mirrorAbortRef.current?.abort();
5912
6629
  onClose();
5913
6630
  if (uuid) onComplete({ overlayId: uuid });
@@ -5916,12 +6633,32 @@ function SEImportWizard({
5916
6633
  } finally {
5917
6634
  setImporting(false);
5918
6635
  }
5919
- }, [notify, onClose, onComplete, options.name, result, saveOverlay]);
6636
+ }, [
6637
+ notify,
6638
+ onClose,
6639
+ onComplete,
6640
+ options.name,
6641
+ result,
6642
+ saveOverlay,
6643
+ seClient,
6644
+ overlayQueue,
6645
+ currentOverlayIndex,
6646
+ totalOverlaysToImport,
6647
+ loadBootstrapAndKickoff
6648
+ ]);
5920
6649
  const visibleSteps = useMemo5(() => {
5921
6650
  const showMirrorStep = options.mirrorAssets && (assetUrls.length > 0 || mirrorRows.length > 0 || step === "mirror");
5922
- const steps = CORE_STEPS.filter(
5923
- (item) => item.key !== "mirror" || showMirrorStep
5924
- );
6651
+ if (step === "mode") return [];
6652
+ const onUrlFlow = entryMode === "url" || step === "url";
6653
+ const onJwtFlow = !onUrlFlow;
6654
+ const steps = CORE_STEPS.filter((item) => {
6655
+ if (item.key === "mode") return false;
6656
+ if (item.key === "mirror" && !showMirrorStep) return false;
6657
+ if (item.key === "connect" && !onJwtFlow) return false;
6658
+ if (item.key === "pick" && !onJwtFlow) return false;
6659
+ if (item.key === "url" && !onUrlFlow) return false;
6660
+ return true;
6661
+ });
5925
6662
  if (originalReviewItems.length > 0)
5926
6663
  steps.splice(Math.max(steps.length - 1, 0), 0, {
5927
6664
  key: "review",
@@ -5930,9 +6667,11 @@ function SEImportWizard({
5930
6667
  return steps;
5931
6668
  }, [
5932
6669
  assetUrls.length,
6670
+ entryMode,
5933
6671
  mirrorRows.length,
5934
6672
  options.mirrorAssets,
5935
6673
  originalReviewItems.length,
6674
+ seClient,
5936
6675
  step
5937
6676
  ]);
5938
6677
  const goBack = () => {
@@ -5941,14 +6680,22 @@ function SEImportWizard({
5941
6680
  setReviewIndex(reviewIndex - 1);
5942
6681
  return;
5943
6682
  }
6683
+ if (currentIndex === 0 && (step === "connect" || step === "url")) {
6684
+ setStep("mode");
6685
+ setEntryMode(null);
6686
+ return;
6687
+ }
5944
6688
  if (currentIndex > 0) setStep(visibleSteps[currentIndex - 1].key);
5945
6689
  };
5946
6690
  const goNext = () => {
5947
6691
  if (step === "connect") {
5948
6692
  if (!seClient) return void handleConnect();
5949
- setStep("url");
6693
+ setStep("pick");
5950
6694
  return;
5951
6695
  }
6696
+ if (step === "pick") {
6697
+ return void handlePickConfirm();
6698
+ }
5952
6699
  if (step === "url") return void handleLoad();
5953
6700
  if (step === "review") {
5954
6701
  if (!currentItem) return;
@@ -5971,6 +6718,11 @@ function SEImportWizard({
5971
6718
  const nextLabel = (() => {
5972
6719
  if (step === "connect")
5973
6720
  return connecting ? "Connecting..." : seClient ? "Continue" : "Connect";
6721
+ if (step === "pick") {
6722
+ if (loading) return "Loading overlay...";
6723
+ const n = selectedOverlayIds.size;
6724
+ return n > 1 ? `Import ${n} overlays` : "Import overlay";
6725
+ }
5974
6726
  if (step === "url") return loading ? "Loading..." : "Load overlay";
5975
6727
  if (step === "mirror") return mirrorRunning ? "Working..." : "Continue";
5976
6728
  if (step === "review")
@@ -5982,6 +6734,8 @@ function SEImportWizard({
5982
6734
  const nextDisabled = (() => {
5983
6735
  if (step === "connect")
5984
6736
  return connecting || !seClient && !jwt.trim();
6737
+ if (step === "pick")
6738
+ return loading || overlaysLoading || selectedOverlayIds.size === 0;
5985
6739
  if (step === "url") return loading || !url.trim();
5986
6740
  if (step === "mirror") return mirrorRunning;
5987
6741
  if (step === "review")
@@ -5990,8 +6744,34 @@ function SEImportWizard({
5990
6744
  return false;
5991
6745
  })();
5992
6746
  const activeIndex = visibleSteps.findIndex((item) => item.key === step);
6747
+ const batchActive = totalOverlaysToImport > 1 && step !== "mode" && step !== "pick" && step !== "connect" && step !== "url";
6748
+ const currentOverlayName = overlayList?.find((o) => o._id === Array.from(selectedOverlayIds).filter((id) => !overlayQueue.includes(id))[currentOverlayIndex - 1])?.name;
5993
6749
  return /* @__PURE__ */ jsx13("div", { className: "se-import", children: /* @__PURE__ */ jsxs7("div", { className: "se-import__shell", children: [
5994
- /* @__PURE__ */ jsx13("header", { className: "se-import__header", children: /* @__PURE__ */ jsx13("div", { className: "se-import-stepper", children: visibleSteps.map((item, index) => /* @__PURE__ */ jsxs7(
6750
+ batchActive && /* @__PURE__ */ jsxs7(
6751
+ "div",
6752
+ {
6753
+ style: {
6754
+ marginBottom: 16,
6755
+ padding: "10px 16px",
6756
+ border: "1px solid var(--se-border-strong)",
6757
+ borderRadius: 8,
6758
+ background: "rgba(255, 64, 118, 0.08)",
6759
+ color: "var(--se-text)",
6760
+ fontSize: 13,
6761
+ fontWeight: 600,
6762
+ textAlign: "center"
6763
+ },
6764
+ children: [
6765
+ "Importing overlay ",
6766
+ currentOverlayIndex,
6767
+ " of ",
6768
+ totalOverlaysToImport,
6769
+ currentOverlayName ? ` \xB7 ${currentOverlayName}` : "",
6770
+ overlayQueue.length > 0 ? ` \xB7 ${overlayQueue.length} more queued` : ""
6771
+ ]
6772
+ }
6773
+ ),
6774
+ visibleSteps.length > 0 && /* @__PURE__ */ jsx13("header", { className: "se-import__header", children: /* @__PURE__ */ jsx13("div", { className: "se-import-stepper", children: visibleSteps.map((item, index) => /* @__PURE__ */ jsxs7(
5995
6775
  "div",
5996
6776
  {
5997
6777
  className: stepClass(
@@ -6007,6 +6787,19 @@ function SEImportWizard({
6007
6787
  item.key
6008
6788
  )) }) }),
6009
6789
  /* @__PURE__ */ jsxs7("main", { className: "se-import__content", children: [
6790
+ step === "mode" && /* @__PURE__ */ jsx13(
6791
+ StepModePicker,
6792
+ {
6793
+ onChooseJwt: () => {
6794
+ setEntryMode("jwt");
6795
+ setStep("connect");
6796
+ },
6797
+ onChooseUrl: () => {
6798
+ setEntryMode("url");
6799
+ setStep("url");
6800
+ }
6801
+ }
6802
+ ),
6010
6803
  step === "connect" && /* @__PURE__ */ jsx13(
6011
6804
  StepConnect,
6012
6805
  {
@@ -6017,7 +6810,23 @@ function SEImportWizard({
6017
6810
  channels,
6018
6811
  selectedChannelId,
6019
6812
  setSelectedChannelId,
6020
- onUseUrl: () => setStep("url")
6813
+ onUseUrl: () => {
6814
+ setEntryMode("url");
6815
+ setStep("url");
6816
+ }
6817
+ }
6818
+ ),
6819
+ step === "pick" && /* @__PURE__ */ jsx13(
6820
+ StepPickOverlay,
6821
+ {
6822
+ overlays: overlayList,
6823
+ loading: overlaysLoading,
6824
+ error: overlaysError,
6825
+ selectedIds: selectedOverlayIds,
6826
+ toggleId: toggleOverlaySelection,
6827
+ selectAll: selectAllOverlays,
6828
+ clearAll: clearAllOverlays,
6829
+ onRetry: () => seClient && void loadOverlayList(seClient)
6021
6830
  }
6022
6831
  ),
6023
6832
  step === "url" && /* @__PURE__ */ jsx13(
@@ -6109,7 +6918,7 @@ function SEImportWizard({
6109
6918
  label: translate("action.cancel", "Cancel")
6110
6919
  }
6111
6920
  ),
6112
- activeIndex > 0 && /* @__PURE__ */ jsx13(
6921
+ step !== "mode" && /* @__PURE__ */ jsx13(
6113
6922
  LSButton,
6114
6923
  {
6115
6924
  type: "button",
@@ -6119,7 +6928,7 @@ function SEImportWizard({
6119
6928
  label: "Back"
6120
6929
  }
6121
6930
  ),
6122
- /* @__PURE__ */ jsx13(
6931
+ step !== "mode" && /* @__PURE__ */ jsx13(
6123
6932
  LSButton,
6124
6933
  {
6125
6934
  type: "button",
@@ -6141,7 +6950,10 @@ function extractErrorMessage(err) {
6141
6950
  // src/se-import/index.ts
6142
6951
  var REVIEW_REASONS = {
6143
6952
  "se-widget-bit-boss": "No native Lumia equivalent \u2014 bit boss fight bar with HP/damage states.",
6144
- "se-widget-hype-cup": "No native Lumia equivalent \u2014 Box2D physics stacking animation.",
6953
+ // `se-widget-hype-cup` intentionally omitted it now routes to the native
6954
+ // `tipjar` module (see dispatcher.ts) and lands with `partial` status, so it
6955
+ // never reaches the placeholder-reason fallback. If a future feature flag
6956
+ // disables tipjar auto-import, add an entry to FLAG_OFF_REASONS instead.
6145
6957
  "se-widget-train": "No native Lumia equivalent \u2014 animated subscriber/event train.",
6146
6958
  "se-widget-media-share": "No native Lumia equivalent \u2014 viewer-submitted media player.",
6147
6959
  "se-widget-contest": "No native Lumia equivalent \u2014 SE-specific contest widget.",
@@ -6150,13 +6962,17 @@ var REVIEW_REASONS = {
6150
6962
  "se-widget-top-cheerers-list": 'Lumia loyaltyleaderboard needs a "source" extension before it can render cheer rankings.',
6151
6963
  "se-widget-top-gifters-list": 'Lumia loyaltyleaderboard needs a "source" extension before it can render gifter rankings.',
6152
6964
  "se-widget-top-tippers-list": 'Lumia loyaltyleaderboard needs a "source" extension before it can render tipper rankings.',
6153
- "se-widget-snow": "Seasonal effect \u2014 needs a particle template in Lumia.",
6154
- "se-widget-halloween": "Seasonal effect \u2014 needs a Lumia template.",
6155
- "se-widget-halloween-2019": "Seasonal effect \u2014 needs a Lumia template.",
6156
- "se-widget-xmas": "Seasonal effect \u2014 needs a Lumia template.",
6157
- "se-widget-valentine": "Seasonal effect \u2014 needs a Lumia template.",
6158
- "se-widget-easter": "Seasonal effect \u2014 needs a Lumia template.",
6159
- "se-widget-world-cup": "Seasonal effect \u2014 needs a Lumia template."
6965
+ // Merch products rotator imports as an empty slideshow with the title
6966
+ // "Merch products (add images manually)". Lumia's Fourthwall integration is
6967
+ // webhook-driven (events flow in) but doesn't fetch the user's shop catalog,
6968
+ // so we can't pre-populate the slideshow items. The streamer adds their
6969
+ // product images by hand in the slideshow editor. Status = 'partial' (see
6970
+ // dispatcher.ts), so this string surfaces in the review step.
6971
+ "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."
6972
+ // Seasonal widgets (snow / halloween / halloween-2019 / xmas / valentine / easter
6973
+ // / world-cup) intentionally omitted — they route directly to image/slideshow/
6974
+ // video modules now (see dispatcher.ts SEASONAL_IMAGE_TYPES), so they always
6975
+ // produce a `direct` status and never need a fallback review reason.
6160
6976
  };
6161
6977
  function reasonFor(seType, status, flaggedOff) {
6162
6978
  if (flaggedOff && FLAG_OFF_REASONS[seType]) return FLAG_OFF_REASONS[seType];