@lumiastream/ui 0.5.3 → 0.5.5

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
@@ -21,7 +21,7 @@ function styleInject(css, { insertAt } = {}) {
21
21
  }
22
22
 
23
23
  // src/lumia-ui.css
24
- styleInject(".ls-color-picker,\n.ls-color-picker *,\n.mui-multi-select,\n.mui-multi-select *,\n.ls-multi-select-value,\n.ls-multi-select-value *,\n.ls-variable-picker,\n.ls-variable-picker *,\n.ls-variable-token,\n.ls-variable-token *,\n.ls-font-picker,\n.ls-font-picker *,\n.mui-ls-input,\n.mui-ls-input * {\n box-sizing: border-box;\n}\n.ls-color-picker button,\n.mui-multi-select button {\n font: inherit;\n letter-spacing: inherit;\n color: inherit;\n margin: 0;\n}\n.ls-color-picker__preview {\n appearance: none;\n -webkit-appearance: none;\n background-color: transparent;\n background-image: none;\n cursor: pointer;\n text-transform: uppercase;\n user-select: none;\n}\n.mui-ls-button.Mui-disabled {\n color: var(--neutralLight2, var(--white2, #cac9d5));\n opacity: 0.45;\n background-color: var(--separator, #0a0821);\n cursor: not-allowed;\n}\n.mui-ls-button {\n border-radius: var(--radius, 1rem);\n padding: 0.375rem 1.5rem;\n text-transform: uppercase;\n}\n.mui-ls-button.MuiButton-sizeSmall {\n padding: 0.25rem 1rem;\n}\n.mui-ls-button.MuiButton-sizeLarge {\n padding: 0.625rem 2rem;\n}\n.mui-ls-button--extended {\n padding-inline: 3rem;\n}\n.mui-ls-button.MuiButton-containedPrimary,\n.mui-ls-button.Mui-disabled.MuiButton-containedPrimary {\n background: var(--primary, #ff4076);\n color: var(--alwayswhite, #fff) !important;\n}\n.mui-ls-button.MuiButton-containedSecondary {\n background-color: var(--secondary, #535395);\n color: var(--alwayswhite, #fff) !important;\n}\n.mui-ls-button.Mui-disabled.MuiButton-containedSecondary {\n background-color: var(--secondary, #535395);\n color: var(--neutralLight2, var(--white2, #cac9d5));\n opacity: 0.4;\n}\n.mui-ls-button.MuiButton-outlinedPrimary {\n color: var(--primary, #ff4076);\n border-color: var(--primary, #ff4076);\n}\n.mui-ls-button.MuiButton-outlinedPrimary:hover {\n border-color: var(--primary, #ff4076);\n background-color: color-mix(in srgb, var(--primary, #ff4076) 12%, transparent);\n}\n.mui-ls-button.MuiButton-outlinedSecondary {\n color: var(--neutralLight1, var(--white, #fff));\n border-color: var(--secondary, #535395);\n}\n.mui-ls-button.MuiButton-outlinedSecondary.Mui-disabled {\n color: var(--neutralLight2, var(--white2, #cac9d5));\n border-color: var(--neutralDark4, var(--cardborder, #393853));\n background-color: var(--transwhite, rgba(255, 255, 255, 0.05));\n opacity: 0.5;\n}\n.mui-ls-button.MuiButton-outlinedSecondary:hover {\n border-color: var(--secondary, #535395);\n background-color: var(--transwhite, rgba(255, 255, 255, 0.05));\n}\n.mui-ls-button.MuiButton-containedError {\n color: var(--alwayswhite, #fff) !important;\n}\n.mui-ls-button.MuiButton-outlinedError {\n color: var(--error, #fd5454);\n border-color: var(--error, #fd5454);\n}\n.mui-ls-button.MuiButton-outlinedError:hover {\n border-color: var(--error, #fd5454);\n background-color: color-mix(in srgb, var(--error, #fd5454) 12%, transparent);\n}\n.mui-ls-button--neutral.mui-ls-button--contained {\n background-color: var(--neutralDark4, var(--cardborder, #393853));\n color: var(--neutralLight1, var(--white, #fff)) !important;\n}\n.mui-ls-button--neutral.mui-ls-button--contained:hover {\n background-color: color-mix(in srgb, var(--neutralDark4, var(--cardborder, #393853)) 82%, var(--neutralLight1, #fff));\n}\n.mui-ls-button--neutral.mui-ls-button--outlined {\n color: var(--neutralLight1, var(--white, #fff));\n border-color: var(--neutralDark4, var(--cardborder, #393853));\n background-color: transparent;\n}\n.mui-ls-button--neutral.mui-ls-button--outlined:hover {\n border-color: var(--neutralLight3, var(--grey, #9392a1));\n background-color: var(--transwhite, rgba(255, 255, 255, 0.05));\n}\n.mui-ls-button--neutral.mui-ls-button--text {\n color: var(--neutralLight1, var(--white, #fff));\n}\n.mui-ls-button--neutral.mui-ls-button--text:hover {\n background-color: var(--transwhite, rgba(255, 255, 255, 0.05));\n}\n.mui-ls-button svg {\n max-height: 24px;\n max-width: 24px;\n}\n.mui-ls-input,\n.ls-font-picker,\n.ls-color-picker__input {\n width: 100%;\n}\n.mui-ls-input,\n.mui-ls-select-form-control,\n.mui-multi-select,\n.ls-font-picker,\n.mui-ls-slider-input-root {\n --ls-control-height: 55px;\n}\n.mui-ls-input .MuiOutlinedInput-root,\n.mui-ls-input .MuiInputBase-root,\n.mui-ls-input .MuiOutlinedInput-notchedOutline,\n.mui-ls-select.MuiOutlinedInput-root,\n.mui-ls-select .MuiOutlinedInput-notchedOutline,\n.mui-multi-select .mui-ls-select.MuiOutlinedInput-root,\n.mui-multi-select .mui-ls-select .MuiOutlinedInput-notchedOutline,\n.ls-font-picker .MuiOutlinedInput-root,\n.ls-font-picker .MuiInputBase-root,\n.ls-font-picker .MuiOutlinedInput-notchedOutline,\n.ls-color-picker__input .MuiOutlinedInput-root,\n.ls-color-picker__input .MuiInputBase-root,\n.ls-color-picker__input .MuiOutlinedInput-notchedOutline {\n border-radius: var(--radius) !important;\n}\n.mui-ls-input .MuiInputBase-root,\n.mui-ls-select.MuiInputBase-root,\n.mui-multi-select .mui-ls-select.MuiInputBase-root,\n.ls-font-picker .MuiInputBase-root {\n min-height: var(--ls-control-height);\n}\n.mui-ls-input.small .MuiInputBase-root {\n min-height: 25px;\n}\n.mui-ls-input.medium .MuiInputBase-root {\n min-height: 35px;\n}\n.mui-ls-input.noMinHeight .MuiInputBase-root {\n min-height: unset;\n}\n.mui-ls-date-picker input[type=datetime-local]::-webkit-calendar-picker-indicator {\n cursor: pointer;\n opacity: 0.9;\n filter: invert(1);\n}\n.mui-ls-select .MuiSelect-select,\n.mui-multi-select .MuiSelect-select {\n display: flex;\n min-height: calc(var(--ls-control-height) - 2px);\n align-items: center;\n box-sizing: border-box;\n border-radius: var(--radius);\n}\n.mui-multi-select .MuiSelect-select {\n min-width: 0;\n}\n.MuiSvgIcon-root svg {\n color: var(--neutralLight1, var(--white, #fff)) !important;\n}\n.mui-ls-input .MuiSvgIcon-root svg path {\n fill: currentcolor;\n stroke: currentcolor;\n}\n.mui-ls-input input::placeholder,\n.mui-ls-input textarea::placeholder {\n color: var(--neutralLight3, var(--grey, #9392a1));\n opacity: 1;\n}\n.mui-ls-input .Mui-focused .MuiOutlinedInput-notchedOutline,\n.mui-ls-input .MuiOutlinedInput-root.Mui-focused .MuiOutlinedInput-notchedOutline {\n border-color: var(--primary, #ff4076) !important;\n}\n.ls-variable-autocomplete-popper .MuiAutocomplete-paper,\n.ls-variable-autocomplete-paper {\n border: 1px solid var(--neutralDark4, var(--cardborder, #393853)) !important;\n border-radius: var(--radius, 1rem) !important;\n background: var(--neutralDark2, var(--cardbackground, #211f45)) !important;\n color: var(--neutralLight1, var(--white, #fff)) !important;\n box-shadow: 0 16px 32px rgba(0, 0, 0, 0.32) !important;\n overflow: hidden !important;\n}\n.ls-variable-autocomplete-popper .MuiAutocomplete-listbox,\n.ls-variable-autocomplete-listbox {\n padding: 0.25rem 0 !important;\n background: var(--neutralDark2, var(--cardbackground, #211f45)) !important;\n color: var(--neutralLight1, var(--white, #fff)) !important;\n}\n.ls-variable-autocomplete-popper .MuiAutocomplete-option,\n.ls-variable-autocomplete-listbox .MuiAutocomplete-option {\n display: flex !important;\n min-height: 44px !important;\n align-items: center !important;\n padding: 0.625rem 1rem !important;\n color: var(--neutralLight1, var(--white, #fff)) !important;\n font-size: 1rem !important;\n line-height: 1.35 !important;\n cursor: pointer !important;\n transition: background-color 120ms ease, color 120ms ease !important;\n}\n.ls-variable-autocomplete-popper .MuiAutocomplete-option > *,\n.ls-variable-autocomplete-listbox .MuiAutocomplete-option > * {\n min-width: 0;\n}\n.ls-variable-autocomplete-popper .MuiAutocomplete-option:hover,\n.ls-variable-autocomplete-popper .MuiAutocomplete-option.Mui-focused,\n.ls-variable-autocomplete-popper .MuiAutocomplete-option[data-focus=true],\n.ls-variable-autocomplete-popper .MuiAutocomplete-option[aria-selected=true],\n.ls-variable-autocomplete-listbox .MuiAutocomplete-option:hover,\n.ls-variable-autocomplete-listbox .MuiAutocomplete-option.Mui-focused,\n.ls-variable-autocomplete-listbox .MuiAutocomplete-option[data-focus=true],\n.ls-variable-autocomplete-listbox .MuiAutocomplete-option[aria-selected=true] {\n background: var( --cardHover, var(--transwhite, rgba(255, 255, 255, 0.05)) ) !important;\n color: var(--neutralLight1, var(--white, #fff)) !important;\n}\n.ls-variable-option {\n gap: 0.75rem;\n}\n.ls-variable-option__thumb {\n width: 40px;\n height: 40px;\n flex: 0 0 40px;\n border-radius: var(--radius, 1rem);\n object-fit: cover;\n}\n.ls-variable-option__thumb--placeholder {\n background: var(--neutralDark4, var(--cardborder, #393853));\n}\n.ls-variable-option__body {\n min-width: 0;\n}\n.ls-variable-option__label {\n color: var(--neutralLight1, var(--white, #fff));\n font-weight: 500;\n line-height: 1.25;\n}\n.ls-variable-option__helper {\n margin-top: 0.125rem;\n color: var(--neutralLight3, var(--grey, #9392a1));\n font-size: 0.875rem;\n line-height: 1.25;\n}\n.ls-variable-autocomplete-popup-indicator,\n.ls-variable-autocomplete-popup-indicator.MuiIconButton-root,\n.ls-variable-autocomplete-popup-indicator svg {\n color: var(--neutralLight1, var(--white, #fff)) !important;\n}\n.ls-variable-autocomplete-popup-indicator svg path {\n fill: currentcolor !important;\n stroke: currentcolor !important;\n}\n.ls-variable-input-adornment,\n.ls-variable-input-adornment.MuiInputAdornment-root {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n height: 24px;\n width: 24px;\n min-width: 24px;\n margin-left: 0.35rem;\n border-radius: var(--radius, 1rem);\n background: var(--primary, #ff4076) !important;\n color: var(--alwayswhite, var(--white, #fff)) !important;\n font-weight: 800;\n line-height: 1;\n letter-spacing: 0;\n cursor: pointer;\n user-select: none;\n}\n.ls-variable-input-adornment svg,\n.ls-variable-input-adornment path {\n color: currentcolor !important;\n fill: currentcolor !important;\n stroke: currentcolor !important;\n}\n.ls-variable-input-adornment:hover,\n.ls-variable-input-adornment.MuiInputAdornment-root:hover {\n filter: brightness(1.08);\n}\n.ls-variable-input-adornment:active,\n.ls-variable-input-adornment.MuiInputAdornment-root:active {\n filter: brightness(0.96);\n}\n.mui-ls-slider-input-root {\n width: 100%;\n}\n.mui-ls-slider-input-fieldset {\n position: relative;\n min-width: 0;\n width: 100%;\n min-height: var(--ls-control-height);\n margin: 0;\n border: 2px solid var(--neutralDark4, var(--cardborder, #393853));\n border-radius: var(--radius, 1rem);\n padding: 0 0.875rem 0.5rem 0.875rem;\n background: transparent;\n color: var(--neutralLight1, var(--white, #fff));\n}\n.mui-ls-slider-input-fieldset:hover {\n border-color: var(--primary, #ff4076);\n}\n.mui-ls-slider-input-legend {\n display: block;\n width: auto;\n max-width: calc(100% - 1rem);\n margin: 0;\n padding: 0 0.25rem;\n color: var(--neutralLight1, var(--white, #fff));\n font-size: 0.875rem;\n line-height: 1.1;\n white-space: nowrap;\n}\n.mui-ls-slider-input-row {\n display: flex;\n min-width: 0;\n min-height: calc(var(--ls-control-height) - 14px);\n align-items: center;\n gap: 1rem;\n}\n.mui-ls-slider-input-row--slider {\n flex-wrap: nowrap;\n}\n.mui-ls-slider-input-row--input {\n justify-content: stretch;\n}\n.mui-ls-slider-input-value.mui-ls-input {\n margin: 0;\n}\n.mui-ls-slider-input-value.mui-ls-input .MuiFormControl-root,\n.mui-ls-slider-input-value.mui-ls-input .MuiTextField-root {\n margin: 0;\n}\n.mui-ls-slider-input-value.mui-ls-input .MuiInputBase-root {\n min-height: 36px;\n}\n.mui-ls-slider-input-value.mui-ls-input .MuiOutlinedInput-notchedOutline {\n border-color: var(--neutralDark4, var(--cardborder, #393853));\n}\n.mui-ls-slider-input-helper {\n display: block;\n margin-top: 0.25rem;\n color: var(--neutralLight3, var(--grey, #9392a1));\n font-size: 0.75rem;\n line-height: 1.25;\n}\n.ls-multi-select-value {\n display: flex;\n width: 100%;\n min-width: 0;\n flex-wrap: nowrap;\n align-items: center;\n justify-content: space-between;\n gap: 0.5rem;\n overflow: hidden;\n white-space: nowrap;\n}\n.ls-multi-select-value__label {\n min-width: 0;\n flex: 1 1 0%;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.ls-multi-select-value__count {\n flex-shrink: 0;\n white-space: nowrap;\n}\n.ls-color-picker__preview {\n position: relative;\n display: inline-block;\n width: 1.5rem;\n height: 1.5rem;\n flex-shrink: 0;\n padding: 0;\n border: 1px solid color-mix(in srgb, #fff 25%, transparent);\n border-radius: var(--radius, 1rem);\n background-size: cover;\n background-position: center;\n background-repeat: no-repeat;\n box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.25);\n color: transparent;\n line-height: 0;\n}\n.ls-color-picker__preview:disabled {\n cursor: not-allowed;\n opacity: 0.5;\n}\n.ls-color-picker__preview:focus {\n outline: none;\n}\n.ls-color-picker__preview:focus-visible {\n box-shadow:\n 0 0 0 2px var(--primary, #ff4076),\n 0 0 0 4px var(--neutralDark1, #1b1a28),\n 0 0 0 1px rgba(0, 0, 0, 0.25);\n}\n.ls-color-picker__gradient circle,\n.ls-color-picker__gradient polyline {\n stroke: #fff;\n}\n.ls-variable-picker {\n --variable-custom-color: var(--customVariables, #69ffd2);\n --variable-function-color: var( --functionVariables, var(--semanticPurple, #b489ff) );\n --variable-normal-color: var(--normalVariables, var(--semanticBlue, #65b8ff));\n display: flex;\n width: 92vw;\n max-width: 1100px;\n min-height: 50vh;\n max-height: 55vh;\n flex-direction: column;\n overflow: hidden;\n padding: 1rem;\n}\n.ls-variable-picker__field {\n margin: 0 0 0.75rem;\n border: 1px solid color-mix(in srgb, var(--primary, #ff4076) 42%, var(--neutralDark4, #393853));\n border-radius: var(--radius, 1rem);\n padding: 0.5rem;\n}\n.ls-variable-picker__legend {\n padding-inline: 0.375rem;\n color: var(--primary, #ff4076);\n font-size: 0.75rem;\n font-weight: 700;\n}\n.ls-variable-picker__intro {\n margin-bottom: 1rem;\n color: var(--neutralLight2, #cac9d5);\n}\n.ls-variable-picker__header,\n.ls-variable-picker__row {\n display: grid;\n grid-template-columns: minmax(300px, 1.5fr) 1fr 1fr;\n gap: 2rem;\n}\n.ls-variable-picker__header {\n margin-bottom: 0.25rem;\n padding-inline: 0.25rem;\n padding-right: 1.7rem;\n color: var(--neutralLight3, #9392a1);\n}\n.ls-variable-picker__list {\n height: 100%;\n overflow: auto;\n padding-right: 1rem;\n}\n.ls-variable-picker__row {\n cursor: pointer;\n align-items: start;\n border-bottom: 1px solid color-mix(in srgb, var(--neutralDark4, #393853) 70%, transparent);\n padding: 1rem 0.25rem;\n}\n.ls-variable-picker__row--no-border {\n border-bottom: 0;\n}\n.ls-variable-picker__token-cell {\n display: flex;\n min-width: 0;\n align-items: center;\n gap: 0.375rem;\n}\n.ls-variable-picker__expand-toggle,\n.ls-variable-picker__expand-spacer {\n width: 24px;\n height: 24px;\n flex: 0 0 24px;\n}\n.ls-variable-picker__expand-toggle {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border: 1px solid color-mix(in srgb, var(--neutralDark4, #393853) 75%, transparent);\n border-radius: var(--radius, 1rem);\n padding: 0;\n background: var(--neutralDark3, var(--containerbackground, #1f1f3a));\n color: var(--neutralLight2, #cac9d5);\n transition:\n background-color 140ms ease,\n border-color 140ms ease,\n color 140ms ease,\n transform 140ms ease;\n}\n.ls-variable-picker__expand-toggle:hover {\n border-color: var(--primary, #ff4076);\n background: color-mix(in srgb, var(--primary, #ff4076) 16%, var(--neutralDark3, #1f1f3a));\n color: var(--neutralLight1, #fff);\n}\n.ls-variable-picker__expand-toggle svg {\n transform: rotate(-90deg);\n transition: transform 140ms ease;\n}\n.ls-variable-picker__expand-toggle--expanded svg {\n transform: rotate(0deg);\n}\n.ls-variable-picker__row:active {\n opacity: 0.8;\n}\n.ls-variable-picker__description {\n max-width: 600px;\n overflow-wrap: break-word;\n line-height: 1.25;\n word-break: break-all;\n}\n.ls-variable-picker__value {\n min-width: 0;\n}\n.ls-variable-picker__value-input {\n width: 100%;\n min-width: 0;\n cursor: text;\n overflow: hidden;\n border: 0;\n background: transparent;\n color: var(--neutralLight1, var(--white, #fff));\n caret-color: transparent;\n font: inherit;\n font-size: 1rem;\n line-height: 1.25;\n outline: 0;\n padding: 0;\n text-overflow: ellipsis;\n}\n.ls-variable-picker__value-input:focus {\n outline: 0;\n}\n.ls-variable-picker__value-input::selection {\n background: color-mix(in srgb, var(--primary, #ff4076) 45%, transparent);\n color: var(--alwayswhite, var(--white, #fff));\n}\n.ls-variable-token {\n display: flex;\n min-width: 0;\n align-items: center;\n}\n.ls-variable-token--suggested {\n color: var(--suggestedVariables, #ffb469);\n}\n.ls-variable-token--custom {\n color: var(--customVariables, #69ffd2);\n}\n.ls-variable-token--function {\n color: var(--functionVariables, var(--semanticPurple, #b489ff));\n}\n.ls-variable-token--system {\n color: var(--normalVariables, var(--semanticBlue, #65b8ff));\n}\n.ls-variable-token__name {\n min-width: 0;\n word-break: break-all;\n}\n.ls-variable-token__badge {\n display: inline-flex;\n margin-left: 0.5rem;\n flex-shrink: 0;\n align-items: center;\n border: 1px solid currentcolor;\n border-radius: var(--radius, 1rem);\n padding-inline: 0.25rem;\n padding-block: 0;\n font-size: 10px;\n font-weight: 600;\n line-height: 1;\n text-transform: uppercase;\n}\n");
24
+ styleInject(".ls-color-picker,\n.ls-color-picker *,\n.mui-multi-select,\n.mui-multi-select *,\n.ls-multi-select-value,\n.ls-multi-select-value *,\n.ls-variable-picker,\n.ls-variable-picker *,\n.ls-variable-token,\n.ls-variable-token *,\n.ls-font-picker,\n.ls-font-picker *,\n.mui-ls-input,\n.mui-ls-input * {\n box-sizing: border-box;\n}\n.ls-color-picker button,\n.mui-multi-select button {\n font: inherit;\n letter-spacing: inherit;\n color: inherit;\n margin: 0;\n}\n.ls-color-picker__preview {\n appearance: none;\n -webkit-appearance: none;\n background-color: transparent;\n background-image: none;\n cursor: pointer;\n text-transform: uppercase;\n user-select: none;\n}\n.mui-ls-button.Mui-disabled {\n color: var(--neutralLight2, var(--white2, #cac9d5));\n opacity: 0.45;\n background-color: var(--separator, #0a0821);\n cursor: not-allowed;\n}\n.mui-ls-button {\n border-radius: var(--radius, 1rem);\n padding: 0.375rem 1.5rem;\n text-transform: uppercase;\n}\n.mui-ls-button.MuiButton-sizeSmall {\n padding: 0.25rem 1rem;\n}\n.mui-ls-button.MuiButton-sizeLarge {\n padding: 0.625rem 2rem;\n}\n.mui-ls-button--extended {\n padding-inline: 3rem;\n}\n.mui-ls-button.MuiButton-containedPrimary,\n.mui-ls-button.Mui-disabled.MuiButton-containedPrimary {\n background: var(--primary, #ff4076);\n color: var(--alwayswhite, #fff) !important;\n}\n.mui-ls-button.MuiButton-containedSecondary {\n background-color: var(--secondary, #535395);\n color: var(--alwayswhite, #fff) !important;\n}\n.mui-ls-button.Mui-disabled.MuiButton-containedSecondary {\n background-color: var(--secondary, #535395);\n color: var(--neutralLight2, var(--white2, #cac9d5));\n opacity: 0.4;\n}\n.mui-ls-button.MuiButton-outlinedPrimary {\n color: var(--primary, #ff4076);\n border-color: var(--primary, #ff4076);\n}\n.mui-ls-button.MuiButton-outlinedPrimary:hover {\n border-color: var(--primary, #ff4076);\n background-color: color-mix(in srgb, var(--primary, #ff4076) 12%, transparent);\n}\n.mui-ls-button.MuiButton-outlinedSecondary {\n color: var(--neutralLight1, var(--white, #fff));\n border-color: var(--secondary, #535395);\n}\n.mui-ls-button.MuiButton-outlinedSecondary.Mui-disabled {\n color: var(--neutralLight2, var(--white2, #cac9d5));\n border-color: var(--neutralDark4, var(--cardborder, #393853));\n background-color: var(--transwhite, rgba(255, 255, 255, 0.05));\n opacity: 0.5;\n}\n.mui-ls-button.MuiButton-outlinedSecondary:hover {\n border-color: var(--secondary, #535395);\n background-color: var(--transwhite, rgba(255, 255, 255, 0.05));\n}\n.mui-ls-button.MuiButton-containedError {\n color: var(--alwayswhite, #fff) !important;\n}\n.mui-ls-button.MuiButton-outlinedError {\n color: var(--error, #fd5454);\n border-color: var(--error, #fd5454);\n}\n.mui-ls-button.MuiButton-outlinedError:hover {\n border-color: var(--error, #fd5454);\n background-color: color-mix(in srgb, var(--error, #fd5454) 12%, transparent);\n}\n.mui-ls-button--neutral.mui-ls-button--contained {\n background-color: var(--neutralDark4, var(--cardborder, #393853));\n color: var(--neutralLight1, var(--white, #fff)) !important;\n}\n.mui-ls-button--neutral.mui-ls-button--contained:hover {\n background-color: color-mix(in srgb, var(--neutralDark4, var(--cardborder, #393853)) 82%, var(--neutralLight1, #fff));\n}\n.mui-ls-button--neutral.mui-ls-button--outlined {\n color: var(--neutralLight1, var(--white, #fff));\n border-color: var(--neutralDark4, var(--cardborder, #393853));\n background-color: transparent;\n}\n.mui-ls-button--neutral.mui-ls-button--outlined:hover {\n border-color: var(--neutralLight3, var(--grey, #9392a1));\n background-color: var(--transwhite, rgba(255, 255, 255, 0.05));\n}\n.mui-ls-button--neutral.mui-ls-button--text {\n color: var(--neutralLight1, var(--white, #fff));\n}\n.mui-ls-button--neutral.mui-ls-button--text:hover {\n background-color: var(--transwhite, rgba(255, 255, 255, 0.05));\n}\n.mui-ls-button svg {\n max-height: 24px;\n max-width: 24px;\n}\n.mui-ls-input,\n.ls-font-picker,\n.ls-color-picker__input {\n width: 100%;\n}\n.mui-ls-input,\n.mui-ls-select-form-control,\n.mui-multi-select,\n.ls-font-picker,\n.mui-ls-slider-input-root {\n --ls-control-height: 55px;\n}\n.mui-ls-input .MuiOutlinedInput-root,\n.mui-ls-input .MuiInputBase-root,\n.mui-ls-input .MuiOutlinedInput-notchedOutline,\n.mui-ls-select.MuiOutlinedInput-root,\n.mui-ls-select .MuiOutlinedInput-notchedOutline,\n.mui-multi-select .mui-ls-select.MuiOutlinedInput-root,\n.mui-multi-select .mui-ls-select .MuiOutlinedInput-notchedOutline,\n.ls-font-picker .MuiOutlinedInput-root,\n.ls-font-picker .MuiInputBase-root,\n.ls-font-picker .MuiOutlinedInput-notchedOutline,\n.ls-color-picker__input .MuiOutlinedInput-root,\n.ls-color-picker__input .MuiInputBase-root,\n.ls-color-picker__input .MuiOutlinedInput-notchedOutline {\n border-radius: var(--radius) !important;\n}\n.mui-ls-input .MuiInputBase-root,\n.mui-ls-select.MuiInputBase-root,\n.mui-multi-select .mui-ls-select.MuiInputBase-root,\n.ls-font-picker .MuiInputBase-root {\n min-height: var(--ls-control-height);\n}\n.mui-ls-input.small .MuiInputBase-root {\n min-height: 25px;\n}\n.mui-ls-input.medium .MuiInputBase-root {\n min-height: 35px;\n}\n.mui-ls-input.noMinHeight .MuiInputBase-root {\n min-height: unset;\n}\n.mui-ls-date-picker input[type=datetime-local]::-webkit-calendar-picker-indicator {\n cursor: pointer;\n opacity: 0.9;\n filter: invert(1);\n}\n.mui-ls-select .MuiSelect-select,\n.mui-multi-select .MuiSelect-select {\n display: flex;\n min-height: calc(var(--ls-control-height) - 2px);\n align-items: center;\n box-sizing: border-box;\n border-radius: var(--radius);\n}\n.mui-multi-select .MuiSelect-select {\n min-width: 0;\n}\n.MuiSvgIcon-root svg {\n color: var(--neutralLight1, var(--white, #fff)) !important;\n}\n.mui-ls-input .MuiSvgIcon-root svg path {\n fill: currentcolor;\n stroke: currentcolor;\n}\n.mui-ls-input input::placeholder,\n.mui-ls-input textarea::placeholder {\n color: var(--neutralLight3, var(--grey, #9392a1));\n opacity: 0.55;\n font-style: italic;\n}\n.mui-ls-input .MuiInputBase-input,\n.mui-ls-input .MuiInputAdornment-root,\n.mui-ls-input .MuiSvgIcon-root,\n.mui-ls-input .MuiFormHelperText-root {\n color: var(--neutralLight1, var(--white, #fff));\n opacity: 1;\n}\n.mui-ls-input .Mui-focused .MuiOutlinedInput-notchedOutline,\n.mui-ls-input .MuiOutlinedInput-root.Mui-focused .MuiOutlinedInput-notchedOutline {\n border-color: var(--primary, #ff4076) !important;\n}\n.ls-variable-autocomplete-popper .MuiAutocomplete-paper,\n.ls-variable-autocomplete-paper {\n border: 1px solid var(--neutralDark4, var(--cardborder, #393853)) !important;\n border-radius: var(--radius, 1rem) !important;\n background: var(--neutralDark2, var(--cardbackground, #211f45)) !important;\n color: var(--neutralLight1, var(--white, #fff)) !important;\n box-shadow: 0 16px 32px rgba(0, 0, 0, 0.32) !important;\n overflow: hidden !important;\n}\n.ls-variable-autocomplete-popper .MuiAutocomplete-listbox,\n.ls-variable-autocomplete-listbox {\n padding: 0.25rem 0 !important;\n background: var(--neutralDark2, var(--cardbackground, #211f45)) !important;\n color: var(--neutralLight1, var(--white, #fff)) !important;\n}\n.ls-variable-autocomplete-popper .MuiAutocomplete-option,\n.ls-variable-autocomplete-listbox .MuiAutocomplete-option {\n display: flex !important;\n min-height: 44px !important;\n align-items: center !important;\n padding: 0.625rem 1rem !important;\n color: var(--neutralLight1, var(--white, #fff)) !important;\n font-size: 1rem !important;\n line-height: 1.35 !important;\n cursor: pointer !important;\n transition: background-color 120ms ease, color 120ms ease !important;\n}\n.ls-variable-autocomplete-popper .MuiAutocomplete-option > *,\n.ls-variable-autocomplete-listbox .MuiAutocomplete-option > * {\n min-width: 0;\n}\n.ls-variable-autocomplete-popper .MuiAutocomplete-option:hover,\n.ls-variable-autocomplete-popper .MuiAutocomplete-option.Mui-focused,\n.ls-variable-autocomplete-popper .MuiAutocomplete-option[data-focus=true],\n.ls-variable-autocomplete-popper .MuiAutocomplete-option[aria-selected=true],\n.ls-variable-autocomplete-listbox .MuiAutocomplete-option:hover,\n.ls-variable-autocomplete-listbox .MuiAutocomplete-option.Mui-focused,\n.ls-variable-autocomplete-listbox .MuiAutocomplete-option[data-focus=true],\n.ls-variable-autocomplete-listbox .MuiAutocomplete-option[aria-selected=true] {\n background: var( --cardHover, var(--transwhite, rgba(255, 255, 255, 0.05)) ) !important;\n color: var(--neutralLight1, var(--white, #fff)) !important;\n}\n.ls-variable-option {\n gap: 0.75rem;\n}\n.ls-variable-option__thumb {\n width: 40px;\n height: 40px;\n flex: 0 0 40px;\n border-radius: var(--radius, 1rem);\n object-fit: cover;\n}\n.ls-variable-option__thumb--placeholder {\n background: var(--neutralDark4, var(--cardborder, #393853));\n}\n.ls-variable-option__body {\n min-width: 0;\n}\n.ls-variable-option__label {\n color: var(--neutralLight1, var(--white, #fff));\n font-weight: 500;\n line-height: 1.25;\n}\n.ls-variable-option__helper {\n margin-top: 0.125rem;\n color: var(--neutralLight3, var(--grey, #9392a1));\n font-size: 0.875rem;\n line-height: 1.25;\n}\n.ls-variable-autocomplete-popup-indicator,\n.ls-variable-autocomplete-popup-indicator.MuiIconButton-root,\n.ls-variable-autocomplete-popup-indicator svg {\n color: var(--neutralLight1, var(--white, #fff)) !important;\n}\n.ls-variable-autocomplete-popup-indicator svg path {\n fill: currentcolor !important;\n stroke: currentcolor !important;\n}\n.ls-variable-input-adornment,\n.ls-variable-input-adornment.MuiInputAdornment-root {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n height: 24px;\n width: 24px;\n min-width: 24px;\n margin-left: 0.35rem;\n border-radius: var(--radius, 1rem);\n background: var(--primary, #ff4076) !important;\n color: var(--alwayswhite, var(--white, #fff)) !important;\n font-weight: 800;\n line-height: 1;\n letter-spacing: 0;\n cursor: pointer;\n user-select: none;\n}\n.ls-variable-input-adornment svg,\n.ls-variable-input-adornment path {\n color: currentcolor !important;\n fill: currentcolor !important;\n stroke: currentcolor !important;\n}\n.ls-variable-input-adornment:hover,\n.ls-variable-input-adornment.MuiInputAdornment-root:hover {\n filter: brightness(1.08);\n}\n.ls-variable-input-adornment:active,\n.ls-variable-input-adornment.MuiInputAdornment-root:active {\n filter: brightness(0.96);\n}\n.mui-ls-slider-input-root {\n width: 100%;\n}\n.mui-ls-slider-input-fieldset {\n position: relative;\n min-width: 0;\n width: 100%;\n min-height: var(--ls-control-height);\n margin: 0;\n border: 2px solid var(--neutralDark4, var(--cardborder, #393853));\n border-radius: var(--radius, 1rem);\n padding: 0 0.875rem 0.5rem 0.875rem;\n background: transparent;\n color: var(--neutralLight1, var(--white, #fff));\n}\n.mui-ls-slider-input-fieldset:hover {\n border-color: var(--primary, #ff4076);\n}\n.mui-ls-slider-input-legend {\n display: block;\n width: auto;\n max-width: calc(100% - 1rem);\n margin: 0;\n padding: 0 0.25rem;\n color: var(--neutralLight1, var(--white, #fff));\n font-size: 0.875rem;\n line-height: 1.1;\n white-space: nowrap;\n}\n.mui-ls-slider-input-row {\n display: flex;\n min-width: 0;\n min-height: calc(var(--ls-control-height) - 14px);\n align-items: center;\n gap: 1rem;\n}\n.mui-ls-slider-input-row--slider {\n flex-wrap: nowrap;\n}\n.mui-ls-slider-input-row--input {\n justify-content: stretch;\n}\n.mui-ls-slider-input-value.mui-ls-input {\n margin: 0;\n}\n.mui-ls-slider-input-value.mui-ls-input .MuiFormControl-root,\n.mui-ls-slider-input-value.mui-ls-input .MuiTextField-root {\n margin: 0;\n}\n.mui-ls-slider-input-value.mui-ls-input .MuiInputBase-root {\n min-height: 36px;\n}\n.mui-ls-slider-input-value.mui-ls-input .MuiOutlinedInput-notchedOutline {\n border-color: var(--neutralDark4, var(--cardborder, #393853));\n}\n.mui-ls-slider-input-helper {\n display: block;\n margin-top: 0.25rem;\n color: var(--neutralLight3, var(--grey, #9392a1));\n font-size: 0.75rem;\n line-height: 1.25;\n}\n.ls-multi-select-value {\n display: flex;\n width: 100%;\n min-width: 0;\n flex-wrap: nowrap;\n align-items: center;\n justify-content: space-between;\n gap: 0.5rem;\n overflow: hidden;\n white-space: nowrap;\n}\n.ls-multi-select-value__label {\n min-width: 0;\n flex: 1 1 0%;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.ls-multi-select-value__count {\n flex-shrink: 0;\n white-space: nowrap;\n}\n.ls-color-picker__preview {\n position: relative;\n display: inline-block;\n width: 1.5rem;\n height: 1.5rem;\n flex-shrink: 0;\n padding: 0;\n border: 1px solid color-mix(in srgb, #fff 25%, transparent);\n border-radius: var(--radius, 1rem);\n background-size: cover;\n background-position: center;\n background-repeat: no-repeat;\n box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.25);\n color: transparent;\n line-height: 0;\n}\n.ls-color-picker__preview:disabled {\n cursor: not-allowed;\n opacity: 0.5;\n}\n.ls-color-picker__preview:focus {\n outline: none;\n}\n.ls-color-picker__preview:focus-visible {\n box-shadow:\n 0 0 0 2px var(--primary, #ff4076),\n 0 0 0 4px var(--neutralDark1, #1b1a28),\n 0 0 0 1px rgba(0, 0, 0, 0.25);\n}\n.ls-color-picker__gradient circle,\n.ls-color-picker__gradient polyline {\n stroke: #fff;\n}\n.ls-variable-picker {\n --variable-custom-color: var(--customVariables, #69ffd2);\n --variable-function-color: var( --functionVariables, var(--semanticPurple, #b489ff) );\n --variable-normal-color: var(--normalVariables, var(--semanticBlue, #65b8ff));\n display: flex;\n width: 92vw;\n max-width: 1100px;\n min-height: 50vh;\n max-height: 55vh;\n flex-direction: column;\n overflow: hidden;\n padding: 1rem;\n}\n.ls-variable-picker__field {\n margin: 0 0 0.75rem;\n border: 1px solid color-mix(in srgb, var(--primary, #ff4076) 42%, var(--neutralDark4, #393853));\n border-radius: var(--radius, 1rem);\n padding: 0.5rem;\n}\n.ls-variable-picker__legend {\n padding-inline: 0.375rem;\n color: var(--primary, #ff4076);\n font-size: 0.75rem;\n font-weight: 700;\n}\n.ls-variable-picker__intro {\n margin-bottom: 1rem;\n color: var(--neutralLight2, #cac9d5);\n}\n.ls-variable-picker__header,\n.ls-variable-picker__row {\n display: grid;\n grid-template-columns: minmax(300px, 1.5fr) 1fr 1fr;\n gap: 2rem;\n}\n.ls-variable-picker__header {\n margin-bottom: 0.25rem;\n padding-inline: 0.25rem;\n padding-right: 1.7rem;\n color: var(--neutralLight3, #9392a1);\n}\n.ls-variable-picker__list {\n height: 100%;\n overflow: auto;\n padding-right: 1rem;\n}\n.ls-variable-picker__row {\n cursor: pointer;\n align-items: start;\n border-bottom: 1px solid color-mix(in srgb, var(--neutralDark4, #393853) 70%, transparent);\n padding: 1rem 0.25rem;\n}\n.ls-variable-picker__row--no-border {\n border-bottom: 0;\n}\n.ls-variable-picker__token-cell {\n display: flex;\n min-width: 0;\n align-items: center;\n gap: 0.375rem;\n}\n.ls-variable-picker__expand-toggle,\n.ls-variable-picker__expand-spacer {\n width: 24px;\n height: 24px;\n flex: 0 0 24px;\n}\n.ls-variable-picker__expand-toggle {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border: 1px solid color-mix(in srgb, var(--neutralDark4, #393853) 75%, transparent);\n border-radius: var(--radius, 1rem);\n padding: 0;\n background: var(--neutralDark3, var(--containerbackground, #1f1f3a));\n color: var(--neutralLight2, #cac9d5);\n transition:\n background-color 140ms ease,\n border-color 140ms ease,\n color 140ms ease,\n transform 140ms ease;\n}\n.ls-variable-picker__expand-toggle:hover {\n border-color: var(--primary, #ff4076);\n background: color-mix(in srgb, var(--primary, #ff4076) 16%, var(--neutralDark3, #1f1f3a));\n color: var(--neutralLight1, #fff);\n}\n.ls-variable-picker__expand-toggle svg {\n transform: rotate(-90deg);\n transition: transform 140ms ease;\n}\n.ls-variable-picker__expand-toggle--expanded svg {\n transform: rotate(0deg);\n}\n.ls-variable-picker__row:active {\n opacity: 0.8;\n}\n.ls-variable-picker__description {\n max-width: 600px;\n overflow-wrap: break-word;\n line-height: 1.25;\n word-break: break-all;\n}\n.ls-variable-picker__value {\n min-width: 0;\n}\n.ls-variable-picker__value-input {\n width: 100%;\n min-width: 0;\n cursor: text;\n overflow: hidden;\n border: 0;\n background: transparent;\n color: var(--neutralLight1, var(--white, #fff));\n caret-color: transparent;\n font: inherit;\n font-size: 1rem;\n line-height: 1.25;\n outline: 0;\n padding: 0;\n text-overflow: ellipsis;\n}\n.ls-variable-picker__value-input:focus {\n outline: 0;\n}\n.ls-variable-picker__value-input::selection {\n background: color-mix(in srgb, var(--primary, #ff4076) 45%, transparent);\n color: var(--alwayswhite, var(--white, #fff));\n}\n.ls-variable-token {\n display: flex;\n min-width: 0;\n align-items: center;\n}\n.ls-variable-token--suggested {\n color: var(--suggestedVariables, #ffb469);\n}\n.ls-variable-token--custom {\n color: var(--customVariables, #69ffd2);\n}\n.ls-variable-token--function {\n color: var(--functionVariables, var(--semanticPurple, #b489ff));\n}\n.ls-variable-token--system {\n color: var(--normalVariables, var(--semanticBlue, #65b8ff));\n}\n.ls-variable-token__name {\n min-width: 0;\n word-break: break-all;\n}\n.ls-variable-token__badge {\n display: inline-flex;\n margin-left: 0.5rem;\n flex-shrink: 0;\n align-items: center;\n border: 1px solid currentcolor;\n border-radius: var(--radius, 1rem);\n padding-inline: 0.25rem;\n padding-block: 0;\n font-size: 10px;\n font-weight: 600;\n line-height: 1;\n text-transform: uppercase;\n}\n");
25
25
 
26
26
  // src/components/LSButton/LSButton.tsx
27
27
  import Button from "@mui/material/Button";
@@ -93,9 +93,6 @@ var LSInput = forwardRef(
93
93
  inputAfterText,
94
94
  maxWidth,
95
95
  className = "",
96
- InputProps: inputPropsProp,
97
- InputLabelProps: inputLabelPropsProp,
98
- inputProps: htmlInputPropsProp,
99
96
  slotProps: slotPropsProp,
100
97
  onChange,
101
98
  onChangeStart,
@@ -130,25 +127,11 @@ var LSInput = forwardRef(
130
127
  onChange?.(event, sanitizedValue);
131
128
  onChangeEnd?.(event, sanitizedValue);
132
129
  };
133
- const resolvedStartAdornment = inputPropsProp?.startAdornment ?? (startAdornment ? /* @__PURE__ */ jsx2(InputAdornment, { position: "start", children: startAdornment }) : inputBeforeText ? /* @__PURE__ */ jsx2(InputAdornment, { position: "start", children: /* @__PURE__ */ jsx2("span", { className: "mui-ls-input-affix", children: inputBeforeText }) }) : void 0);
134
- const resolvedEndAdornment = inputPropsProp?.endAdornment ?? (endAdornment ? /* @__PURE__ */ jsx2(InputAdornment, { position: "end", children: endAdornment }) : inputAfterText ? /* @__PURE__ */ jsx2(InputAdornment, { position: "end", children: /* @__PURE__ */ jsx2("span", { className: "mui-ls-input-affix", children: inputAfterText }) }) : void 0);
135
- const inputSlotProps = {
136
- ...inputPropsProp,
137
- startAdornment: resolvedStartAdornment,
138
- endAdornment: resolvedEndAdornment,
139
- readOnly: rest.readOnly ?? inputPropsProp?.readOnly,
140
- sx: {
141
- ...inputPropsProp?.sx ?? {},
142
- "& input, & textarea": {
143
- textAlign: centerText ? "center" : void 0,
144
- color: textColor ?? void 0
145
- }
146
- }
147
- };
148
- const inputLabelProps = {
149
- shrink: true,
150
- ...inputLabelPropsProp
151
- };
130
+ const callerInputSlot = slotPropsProp?.input ?? {};
131
+ const callerHtmlInputSlot = slotPropsProp?.htmlInput ?? {};
132
+ const callerInputLabelSlot = slotPropsProp?.inputLabel ?? {};
133
+ const resolvedStartAdornment = callerInputSlot.startAdornment ?? (startAdornment ? /* @__PURE__ */ jsx2(InputAdornment, { position: "start", children: startAdornment }) : inputBeforeText ? /* @__PURE__ */ jsx2(InputAdornment, { position: "start", children: /* @__PURE__ */ jsx2("span", { className: "mui-ls-input-affix", children: inputBeforeText }) }) : void 0);
134
+ const resolvedEndAdornment = callerInputSlot.endAdornment ?? (endAdornment ? /* @__PURE__ */ jsx2(InputAdornment, { position: "end", children: endAdornment }) : inputAfterText ? /* @__PURE__ */ jsx2(InputAdornment, { position: "end", children: /* @__PURE__ */ jsx2("span", { className: "mui-ls-input-affix", children: inputAfterText }) }) : void 0);
152
135
  const helperContent = helperText ?? (typeof error === "string" ? error : "");
153
136
  const hasError = typeof error === "string" ? true : Boolean(error);
154
137
  const resolvedMaxWidth = maxWidth ?? style?.maxWidth;
@@ -156,20 +139,24 @@ var LSInput = forwardRef(
156
139
  const slotProps = {
157
140
  ...slotPropsProp ?? {},
158
141
  input: {
159
- ...slotPropsProp?.input ?? {},
160
- ...inputSlotProps,
142
+ ...callerInputSlot,
143
+ startAdornment: resolvedStartAdornment,
144
+ endAdornment: resolvedEndAdornment,
145
+ readOnly: rest.readOnly ?? callerInputSlot.readOnly,
161
146
  sx: {
162
- ...slotPropsProp?.input?.sx ?? {},
163
- ...inputSlotProps.sx
147
+ ...callerInputSlot.sx ?? {},
148
+ "& input, & textarea": {
149
+ textAlign: centerText ? "center" : void 0,
150
+ color: textColor ?? void 0
151
+ }
164
152
  }
165
153
  },
166
154
  inputLabel: {
167
- ...slotPropsProp?.inputLabel ?? {},
168
- ...inputLabelProps
155
+ shrink: true,
156
+ ...callerInputLabelSlot
169
157
  },
170
158
  htmlInput: {
171
- ...slotPropsProp?.htmlInput ?? {},
172
- ...htmlInputPropsProp ?? {}
159
+ ...callerHtmlInputSlot
173
160
  }
174
161
  };
175
162
  const TextFieldComponent = TextField;
@@ -359,15 +346,15 @@ var LSSliderInput = ({
359
346
  onChange: (_event, nextValue) => handleInputChange({ target: { value: nextValue ?? "" } }),
360
347
  value: displayValue ?? "",
361
348
  inputAfterText,
362
- InputProps: {
363
- ...inputProps?.InputProps ?? {},
364
- name,
365
- type: "number",
366
- value: displayValue,
367
- min: displayMinimum,
368
- max: displayMaximum,
369
- step: displayStep,
370
- inputMode: stepPrecision > 0 ? "decimal" : "numeric"
349
+ slotProps: {
350
+ ...inputProps?.slotProps ?? {},
351
+ htmlInput: {
352
+ ...inputProps?.slotProps?.htmlInput ?? {},
353
+ min: displayMinimum,
354
+ max: displayMaximum,
355
+ step: displayStep,
356
+ inputMode: stepPrecision > 0 ? "decimal" : "numeric"
357
+ }
371
358
  },
372
359
  fullWidth: hideSlider ? fullWidth : false,
373
360
  $noMinHeight: false,
@@ -431,6 +418,16 @@ var LSSelect = ({
431
418
  border: "1px solid var(--neutralDark4)",
432
419
  borderRadius: "var(--radius, 1rem)",
433
420
  boxShadow: "0 16px 32px rgba(0, 0, 0, 0.32)",
421
+ "& .MuiList-root": {
422
+ backgroundColor: "var(--neutralDark2)"
423
+ },
424
+ "& .MuiListSubheader-root": {
425
+ backgroundColor: "var(--neutralDark1)",
426
+ color: "var(--neutralLight2)"
427
+ },
428
+ "& .MuiMenuItem-root": {
429
+ color: "var(--neutralLight1)"
430
+ },
434
431
  ...MenuProps?.slotProps?.paper?.sx
435
432
  }
436
433
  },
@@ -442,28 +439,6 @@ var LSSelect = ({
442
439
  ...MenuProps?.slotProps?.list?.sx
443
440
  }
444
441
  }
445
- },
446
- PaperProps: {
447
- ...MenuProps?.PaperProps ?? {},
448
- sx: {
449
- background: "var(--neutralDark2) !important",
450
- backgroundColor: "var(--neutralDark2)",
451
- color: "var(--neutralLight1)",
452
- border: "1px solid var(--neutralDark4)",
453
- borderRadius: "var(--radius, 1rem)",
454
- boxShadow: "0 16px 32px rgba(0, 0, 0, 0.32)",
455
- "& .MuiList-root": {
456
- backgroundColor: "var(--neutralDark2)"
457
- },
458
- "& .MuiListSubheader-root": {
459
- backgroundColor: "var(--neutralDark1)",
460
- color: "var(--neutralLight2)"
461
- },
462
- "& .MuiMenuItem-root": {
463
- color: "var(--neutralLight1)"
464
- },
465
- ...MenuProps?.PaperProps?.sx
466
- }
467
442
  }
468
443
  };
469
444
  return /* @__PURE__ */ jsxs2(
@@ -783,14 +758,7 @@ LSDatePicker.displayName = "LSDatePicker";
783
758
  import { KeyboardArrowDown } from "@mui/icons-material";
784
759
  import Autocomplete from "@mui/material/Autocomplete";
785
760
  import TextField2 from "@mui/material/TextField";
786
- import {
787
- memo,
788
- startTransition,
789
- useEffect as useEffect3,
790
- useMemo as useMemo2,
791
- useRef as useRef2,
792
- useState as useState3
793
- } from "react";
761
+ import { memo, startTransition, useEffect as useEffect3, useMemo as useMemo2, useRef as useRef2, useState as useState3 } from "react";
794
762
  import { jsx as jsx8 } from "react/jsx-runtime";
795
763
  var LSFontPicker = memo(function LSFontPicker2({
796
764
  value,
@@ -848,9 +816,7 @@ var LSFontPicker = memo(function LSFontPicker2({
848
816
  popupIcon: /* @__PURE__ */ jsx8(KeyboardArrowDown, {}),
849
817
  isOptionEqualToValue: (option, selectedValue) => option === selectedValue,
850
818
  onChange: (_event, newValue) => {
851
- handleValueChange(
852
- typeof newValue === "string" ? newValue : `${newValue ?? ""}`
853
- );
819
+ handleValueChange(typeof newValue === "string" ? newValue : `${newValue ?? ""}`);
854
820
  },
855
821
  onInputChange: (_event, newInputValue, reason) => {
856
822
  if (reason === "input" || reason === "clear") {
@@ -907,11 +873,7 @@ var LSFontPicker = memo(function LSFontPicker2({
907
873
  );
908
874
  },
909
875
  renderInput: (params) => {
910
- const {
911
- InputLabelProps: paramsInputLabelProps,
912
- InputProps: paramsInputProps,
913
- ...restParams
914
- } = params;
876
+ const { slotProps: paramsSlotProps = {}, ...restParams } = params;
915
877
  return /* @__PURE__ */ jsx8(
916
878
  TextFieldComponent,
917
879
  {
@@ -928,12 +890,16 @@ var LSFontPicker = memo(function LSFontPicker2({
928
890
  }
929
891
  },
930
892
  slotProps: {
893
+ ...paramsSlotProps,
931
894
  inputLabel: {
932
- shrink: true,
933
- ...paramsInputLabelProps ?? {}
895
+ ...paramsSlotProps.inputLabel ?? {},
896
+ shrink: true
934
897
  },
935
898
  input: {
936
- ...paramsInputProps ?? {}
899
+ ...paramsSlotProps.input ?? {}
900
+ },
901
+ htmlInput: {
902
+ ...paramsSlotProps.htmlInput ?? {}
937
903
  }
938
904
  }
939
905
  }
@@ -976,6 +942,12 @@ var LSMultiSelect = ({
976
942
  /* @__PURE__ */ jsx9("div", { className: "ls-multi-select-value__count", children: `(${selected.length})` })
977
943
  ] }),
978
944
  MenuProps: {
945
+ // The previous version of this also set `PaperProps` here — that
946
+ // prop was removed from Menu in @mui/material v9 and React warns
947
+ // "does not recognize the `PaperProps` prop on a DOM element"
948
+ // when Menu spreads it through. Folded the one unique selector
949
+ // (`& .MuiList-root`) into `slotProps.paper.sx` below; everything
950
+ // else was already duplicated there.
979
951
  slotProps: {
980
952
  paper: {
981
953
  sx: {
@@ -987,7 +959,10 @@ var LSMultiSelect = ({
987
959
  boxShadow: "0 16px 32px rgba(0, 0, 0, 0.32)",
988
960
  backdropFilter: "none",
989
961
  opacity: 1,
990
- maxHeight: "280px !important"
962
+ maxHeight: "280px !important",
963
+ "& .MuiList-root": {
964
+ backgroundColor: "var(--neutralDark2)"
965
+ }
991
966
  }
992
967
  },
993
968
  list: {
@@ -996,21 +971,6 @@ var LSMultiSelect = ({
996
971
  color: "var(--neutralLight1)"
997
972
  }
998
973
  }
999
- },
1000
- PaperProps: {
1001
- sx: {
1002
- background: "var(--neutralDark2) !important",
1003
- backgroundColor: "var(--neutralDark2)",
1004
- color: "var(--neutralLight1)",
1005
- backdropFilter: "none",
1006
- opacity: 1,
1007
- maxHeight: "280px !important",
1008
- border: "1px solid var(--neutralDark4)",
1009
- borderRadius: "var(--radius, 1rem)",
1010
- "& .MuiList-root": {
1011
- backgroundColor: "var(--neutralDark2)"
1012
- }
1013
- }
1014
974
  }
1015
975
  },
1016
976
  children: options.map((option) => /* @__PURE__ */ jsxs4(
@@ -1077,7 +1037,8 @@ import { forwardRef as forwardRef4 } from "react";
1077
1037
  import { jsx as jsx11 } from "react/jsx-runtime";
1078
1038
  var LSTextField = forwardRef4(
1079
1039
  (props, ref) => {
1080
- const { InputLabelProps, InputProps, slotProps, ...rest } = props;
1040
+ const { slotProps, ...rest } = props;
1041
+ const paramsInputLabel = slotProps?.inputLabel ?? {};
1081
1042
  return /* @__PURE__ */ jsx11(
1082
1043
  TextField3,
1083
1044
  {
@@ -1086,18 +1047,9 @@ var LSTextField = forwardRef4(
1086
1047
  className: "mui-ls-input",
1087
1048
  slotProps: {
1088
1049
  ...slotProps ?? {},
1089
- input: {
1090
- ...slotProps?.input ?? {},
1091
- ...InputProps ?? {},
1092
- sx: {
1093
- ...slotProps?.input?.sx ?? {},
1094
- ...InputProps?.sx ?? {}
1095
- }
1096
- },
1097
1050
  inputLabel: {
1098
- ...slotProps?.inputLabel ?? {},
1099
- shrink: true,
1100
- ...InputLabelProps ?? {}
1051
+ ...paramsInputLabel,
1052
+ shrink: true
1101
1053
  }
1102
1054
  },
1103
1055
  ref
@@ -2256,14 +2208,12 @@ var VariableInputTextField = forwardRef5(
2256
2208
  containerRef,
2257
2209
  showVariableIcon
2258
2210
  }, ref) => {
2259
- const inputPropsInputProps = inputProps?.InputProps ?? {};
2260
- const paramsInputProps = params?.InputProps ?? {};
2261
- const inputPropsSlotInputProps = inputProps?.slotProps?.input ?? {};
2262
- const paramsSlotInputProps = params?.slotProps?.input ?? {};
2211
+ const inputPropsSlotInput = inputProps?.slotProps?.input ?? {};
2212
+ const paramsSlotInput = params?.slotProps?.input ?? {};
2263
2213
  const explicitStartAdornment = inputProps?.startAdornment;
2264
2214
  const resolvedExplicitStartAdornment = explicitStartAdornment ? /* @__PURE__ */ jsx12(InputAdornment2, { position: "start", children: explicitStartAdornment }) : void 0;
2265
- const startAdornment = paramsInputProps.startAdornment ?? paramsSlotInputProps.startAdornment ?? inputPropsInputProps.startAdornment ?? inputPropsSlotInputProps.startAdornment ?? resolvedExplicitStartAdornment;
2266
- const endAdornment = paramsInputProps.endAdornment ?? paramsSlotInputProps.endAdornment ?? inputPropsInputProps.endAdornment ?? inputPropsSlotInputProps.endAdornment;
2215
+ const startAdornment = paramsSlotInput.startAdornment ?? inputPropsSlotInput.startAdornment ?? resolvedExplicitStartAdornment;
2216
+ const endAdornment = paramsSlotInput.endAdornment ?? inputPropsSlotInput.endAdornment;
2267
2217
  return /* @__PURE__ */ jsx12(
2268
2218
  LSTextField,
2269
2219
  {
@@ -2285,31 +2235,27 @@ var VariableInputTextField = forwardRef5(
2285
2235
  onChange: onChange ? (e) => onChange(e.target.value) : void 0,
2286
2236
  ...inputProps,
2287
2237
  ...params,
2288
- InputProps: {
2289
- ...inputPropsSlotInputProps,
2290
- ...inputPropsInputProps,
2291
- ...paramsSlotInputProps,
2292
- ...paramsInputProps,
2293
- startAdornment,
2294
- endAdornment: /* @__PURE__ */ jsxs5(Fragment3, { children: [
2295
- endAdornment ?? null,
2296
- showVariableIcon ? /* @__PURE__ */ jsx12(
2297
- Tooltip,
2298
- {
2299
- title: t("chatbot.allowed-variables", "Allowed Variables"),
2300
- children: /* @__PURE__ */ jsx12(
2301
- InputAdornment2,
2302
- {
2303
- position: "end",
2304
- onClick: clickedVariableIcon,
2305
- ref: containerRef,
2306
- className: "ls-variable-input-adornment",
2307
- children: "{}"
2308
- }
2309
- )
2310
- }
2311
- ) : null
2312
- ] })
2238
+ slotProps: {
2239
+ ...inputProps?.slotProps ?? {},
2240
+ ...params?.slotProps ?? {},
2241
+ input: {
2242
+ ...inputPropsSlotInput,
2243
+ ...paramsSlotInput,
2244
+ startAdornment,
2245
+ endAdornment: /* @__PURE__ */ jsxs5(Fragment3, { children: [
2246
+ endAdornment ?? null,
2247
+ showVariableIcon ? /* @__PURE__ */ jsx12(Tooltip, { title: t("chatbot.allowed-variables", "Allowed Variables"), children: /* @__PURE__ */ jsx12(
2248
+ InputAdornment2,
2249
+ {
2250
+ position: "end",
2251
+ onClick: clickedVariableIcon,
2252
+ ref: containerRef,
2253
+ className: "ls-variable-input-adornment",
2254
+ children: "{}"
2255
+ }
2256
+ ) }) : null
2257
+ ] })
2258
+ }
2313
2259
  },
2314
2260
  inputRef: ref
2315
2261
  }
@@ -3133,7 +3079,7 @@ var resolveChatterProfileUrlWithResolvers = async ({
3133
3079
  };
3134
3080
 
3135
3081
  // src/se-import/index.ts
3136
- import { nanoid as nanoid10 } from "nanoid";
3082
+ import { nanoid as nanoid8 } from "nanoid";
3137
3083
 
3138
3084
  // src/se-import/translate.ts
3139
3085
  var SE_TO_LUMIA_PLACEHOLDER = {
@@ -4184,26 +4130,55 @@ function mapText(widget, ctx) {
4184
4130
  ctx
4185
4131
  );
4186
4132
  }
4133
+ function seInnerAutoFit(widget, innerCss, mode) {
4134
+ const isInnerAutoWidth = innerCss?.width === "auto";
4135
+ const isInnerAutoHeight = innerCss?.height === "auto";
4136
+ if (mode === "width-drives-both") {
4137
+ if (!isInnerAutoWidth) return {};
4138
+ return { objectFit: "contain", autoWidth: true, autoHeight: true };
4139
+ }
4140
+ if (!isInnerAutoWidth && !isInnerAutoHeight) return {};
4141
+ const result = { objectFit: "contain" };
4142
+ if (isInnerAutoWidth && !isExplicitPxSize(widget.css?.width))
4143
+ result.autoWidth = true;
4144
+ if (isInnerAutoHeight && !isExplicitPxSize(widget.css?.height))
4145
+ result.autoHeight = true;
4146
+ return result;
4147
+ }
4148
+ function applySeAutoFitBounds(unit, fit) {
4149
+ const bounds = unit.layer.bounds;
4150
+ if (fit.autoWidth) bounds.autoWidth = true;
4151
+ if (fit.autoHeight) bounds.autoHeight = true;
4152
+ }
4187
4153
  function mapImage(widget, ctx) {
4154
+ const fit = seInnerAutoFit(widget, widget.image?.css, "inner-axis-gated");
4188
4155
  const unit = buildUnit(
4189
4156
  widget,
4190
4157
  "image",
4191
4158
  {
4192
4159
  content: {
4193
4160
  src: widget.image?.src ?? "",
4194
- loop: true
4161
+ loop: true,
4162
+ ...fit.objectFit ? { objectFit: fit.objectFit } : {}
4195
4163
  }
4196
4164
  },
4197
4165
  ctx
4198
4166
  );
4199
- const innerWidth = widget.image?.css?.width;
4200
- const innerHeight = widget.image?.css?.height;
4201
- const bounds = unit.layer.bounds;
4202
- if (innerWidth === "auto") bounds.autoWidth = true;
4203
- if (innerHeight === "auto") bounds.autoHeight = true;
4167
+ applySeAutoFitBounds(unit, fit);
4204
4168
  return unit;
4205
4169
  }
4170
+ function isExplicitPxSize(value) {
4171
+ if (value == null) return false;
4172
+ if (typeof value === "number") return Number.isFinite(value) && value > 0;
4173
+ if (typeof value !== "string") return false;
4174
+ const s = value.trim().toLowerCase();
4175
+ if (s === "" || s === "auto") return false;
4176
+ if (s.endsWith("%")) return false;
4177
+ const parsed = parseFloat(s);
4178
+ return Number.isFinite(parsed) && parsed > 0;
4179
+ }
4206
4180
  function mapVideo(widget, ctx) {
4181
+ const fit = seInnerAutoFit(widget, widget.video?.css, "width-drives-both");
4207
4182
  const unit = buildUnit(
4208
4183
  widget,
4209
4184
  "video",
@@ -4217,6 +4192,7 @@ function mapVideo(widget, ctx) {
4217
4192
  },
4218
4193
  ctx
4219
4194
  );
4195
+ applySeAutoFitBounds(unit, fit);
4220
4196
  unit.layer.bounds.autoHeight = true;
4221
4197
  return unit;
4222
4198
  }
@@ -4276,118 +4252,157 @@ function mapReadout(widget, fallbackVar, ctx) {
4276
4252
 
4277
4253
  // src/se-import/mappers/alert.ts
4278
4254
  import { LumiaAlertConfigs } from "@lumiastream/lumia-types";
4255
+ import { nanoid as nanoid2 } from "nanoid";
4256
+
4257
+ // src/se-import/import-utils.ts
4258
+ function pxToNumber(raw, fallback) {
4259
+ if (typeof raw === "number" && Number.isFinite(raw)) return raw;
4260
+ if (typeof raw !== "string") return fallback;
4261
+ const trimmed = raw.trim();
4262
+ if (!trimmed) return fallback;
4263
+ if (trimmed.endsWith("px")) {
4264
+ const n2 = parseFloat(trimmed.slice(0, -2));
4265
+ return Number.isFinite(n2) ? n2 : fallback;
4266
+ }
4267
+ const n = parseFloat(trimmed);
4268
+ return Number.isFinite(n) ? n : fallback;
4269
+ }
4270
+ function parseDurationMs(raw, fallbackMs) {
4271
+ if (typeof raw === "number" && Number.isFinite(raw)) return raw;
4272
+ if (typeof raw !== "string") return fallbackMs;
4273
+ const trimmed = raw.trim();
4274
+ if (!trimmed) return fallbackMs;
4275
+ if (trimmed.endsWith("ms")) {
4276
+ const n2 = parseFloat(trimmed.slice(0, -2));
4277
+ return Number.isFinite(n2) ? n2 : fallbackMs;
4278
+ }
4279
+ if (trimmed.endsWith("s")) {
4280
+ const n2 = parseFloat(trimmed.slice(0, -1));
4281
+ return Number.isFinite(n2) ? n2 * 1e3 : fallbackMs;
4282
+ }
4283
+ const n = parseFloat(trimmed);
4284
+ return Number.isFinite(n) ? n : fallbackMs;
4285
+ }
4286
+ function buildImportDescription(head, sourceUrl) {
4287
+ if (typeof sourceUrl === "string" && sourceUrl.trim().length > 0) {
4288
+ return `${head}
4289
+ Source: ${sourceUrl.trim()}`;
4290
+ }
4291
+ return head;
4292
+ }
4279
4293
 
4280
4294
  // src/se-import/mappers/provider-alerts.ts
4295
+ import { LumiaAlertValues } from "@lumiastream/lumia-types";
4281
4296
  var ALERT_BOX_KEYS = {
4282
4297
  twitch: {
4283
- follower: ["twitch-follower"],
4284
- subscriber: ["twitch-subscriber"],
4285
- tip: ["lumiastream-donation", "streamelements-donation"],
4286
- cheer: ["twitch-bits"],
4287
- raid: ["twitch-raid"],
4288
- merch: ["fourthwall-shopOrder"],
4289
- purchase: ["fourthwall-shopOrder"],
4290
- charityCampaignDonation: ["twitch-charityDonation"]
4298
+ follower: [LumiaAlertValues.TWITCH_FOLLOWER],
4299
+ subscriber: [LumiaAlertValues.TWITCH_SUBSCRIBER],
4300
+ tip: [LumiaAlertValues.LUMIASTREAM_DONATION, LumiaAlertValues.STREAMELEMENTS_DONATION],
4301
+ cheer: [LumiaAlertValues.TWITCH_BITS],
4302
+ raid: [LumiaAlertValues.TWITCH_RAID],
4303
+ merch: [LumiaAlertValues.FOURTHWALL_SHOPORDER],
4304
+ purchase: [LumiaAlertValues.FOURTHWALL_SHOPORDER],
4305
+ charityCampaignDonation: [LumiaAlertValues.TWITCH_CHARITY_DONATION]
4291
4306
  },
4292
4307
  kick: {
4293
- follower: ["kick-follower"],
4294
- subscriber: ["kick-subscriber"],
4295
- tip: ["lumiastream-donation", "streamelements-donation"],
4296
- cheer: ["kick-kicks"],
4297
- raid: ["kick-host"],
4298
- host: ["kick-host"],
4299
- merch: ["fourthwall-shopOrder"],
4300
- purchase: ["fourthwall-shopOrder"]
4308
+ follower: [LumiaAlertValues.KICK_FOLLOWER],
4309
+ subscriber: [LumiaAlertValues.KICK_SUBSCRIBER],
4310
+ tip: [LumiaAlertValues.LUMIASTREAM_DONATION, LumiaAlertValues.STREAMELEMENTS_DONATION],
4311
+ cheer: [LumiaAlertValues.KICK_KICKS],
4312
+ raid: [LumiaAlertValues.KICK_HOST],
4313
+ host: [LumiaAlertValues.KICK_HOST],
4314
+ merch: [LumiaAlertValues.FOURTHWALL_SHOPORDER],
4315
+ purchase: [LumiaAlertValues.FOURTHWALL_SHOPORDER]
4301
4316
  },
4302
4317
  youtube: {
4303
- follower: ["youtube-subscriber"],
4304
- subscriber: ["youtube-member"],
4305
- tip: ["lumiastream-donation", "streamelements-donation"],
4306
- cheer: ["youtube-superchat"],
4307
- merch: ["fourthwall-shopOrder"],
4308
- purchase: ["fourthwall-shopOrder"]
4318
+ follower: [LumiaAlertValues.YOUTUBE_SUBSCRIBER],
4319
+ subscriber: [LumiaAlertValues.YOUTUBE_MEMBER],
4320
+ tip: [LumiaAlertValues.LUMIASTREAM_DONATION, LumiaAlertValues.STREAMELEMENTS_DONATION],
4321
+ cheer: [LumiaAlertValues.YOUTUBE_SUPERCHAT],
4322
+ merch: [LumiaAlertValues.FOURTHWALL_SHOPORDER],
4323
+ purchase: [LumiaAlertValues.FOURTHWALL_SHOPORDER]
4309
4324
  }
4310
4325
  };
4311
4326
  var KAPPAGEN_KEYS = {
4312
4327
  twitch: {
4313
- follower: "twitch-follower",
4314
- subscriber: "twitch-subscriber",
4315
- cheer: "twitch-bits",
4316
- raid: "twitch-raid",
4317
- tip: "lumiastream-donation"
4328
+ follower: LumiaAlertValues.TWITCH_FOLLOWER,
4329
+ subscriber: LumiaAlertValues.TWITCH_SUBSCRIBER,
4330
+ cheer: LumiaAlertValues.TWITCH_BITS,
4331
+ raid: LumiaAlertValues.TWITCH_RAID,
4332
+ tip: LumiaAlertValues.LUMIASTREAM_DONATION
4318
4333
  },
4319
4334
  kick: {
4320
- follower: "kick-follower",
4321
- subscriber: "kick-subscriber",
4322
- cheer: "kick-kicks",
4323
- raid: "kick-host",
4324
- tip: "lumiastream-donation"
4335
+ follower: LumiaAlertValues.KICK_FOLLOWER,
4336
+ subscriber: LumiaAlertValues.KICK_SUBSCRIBER,
4337
+ cheer: LumiaAlertValues.KICK_KICKS,
4338
+ raid: LumiaAlertValues.KICK_HOST,
4339
+ tip: LumiaAlertValues.LUMIASTREAM_DONATION
4325
4340
  },
4326
4341
  youtube: {
4327
- follower: "youtube-subscriber",
4328
- subscriber: "youtube-member",
4329
- cheer: "youtube-superchat",
4330
- tip: "lumiastream-donation"
4342
+ follower: LumiaAlertValues.YOUTUBE_SUBSCRIBER,
4343
+ subscriber: LumiaAlertValues.YOUTUBE_MEMBER,
4344
+ cheer: LumiaAlertValues.YOUTUBE_SUPERCHAT,
4345
+ tip: LumiaAlertValues.LUMIASTREAM_DONATION
4331
4346
  }
4332
4347
  };
4333
4348
  var STREAMBOSS_LISTENER_KEYS = {
4334
4349
  twitch: {
4335
- "follower-latest": ["twitch-follower"],
4336
- "subscriber-latest": ["twitch-subscriber"],
4337
- "tip-latest": ["lumiastream-donation", "streamelements-donation"],
4338
- "cheer-latest": ["twitch-bits"],
4339
- "raid-latest": ["twitch-raid"]
4350
+ "follower-latest": [LumiaAlertValues.TWITCH_FOLLOWER],
4351
+ "subscriber-latest": [LumiaAlertValues.TWITCH_SUBSCRIBER],
4352
+ "tip-latest": [LumiaAlertValues.LUMIASTREAM_DONATION, LumiaAlertValues.STREAMELEMENTS_DONATION],
4353
+ "cheer-latest": [LumiaAlertValues.TWITCH_BITS],
4354
+ "raid-latest": [LumiaAlertValues.TWITCH_RAID]
4340
4355
  },
4341
4356
  kick: {
4342
- "follower-latest": ["kick-follower"],
4343
- "subscriber-latest": ["kick-subscriber"],
4344
- "tip-latest": ["lumiastream-donation", "streamelements-donation"],
4345
- "cheer-latest": ["kick-kicks"],
4346
- "raid-latest": ["kick-host"]
4357
+ "follower-latest": [LumiaAlertValues.KICK_FOLLOWER],
4358
+ "subscriber-latest": [LumiaAlertValues.KICK_SUBSCRIBER],
4359
+ "tip-latest": [LumiaAlertValues.LUMIASTREAM_DONATION, LumiaAlertValues.STREAMELEMENTS_DONATION],
4360
+ "cheer-latest": [LumiaAlertValues.KICK_KICKS],
4361
+ "raid-latest": [LumiaAlertValues.KICK_HOST]
4347
4362
  },
4348
4363
  youtube: {
4349
- "follower-latest": ["youtube-subscriber"],
4350
- "subscriber-latest": ["youtube-member"],
4351
- "tip-latest": ["lumiastream-donation", "streamelements-donation"],
4352
- "cheer-latest": ["youtube-superchat"]
4364
+ "follower-latest": [LumiaAlertValues.YOUTUBE_SUBSCRIBER],
4365
+ "subscriber-latest": [LumiaAlertValues.YOUTUBE_MEMBER],
4366
+ "tip-latest": [LumiaAlertValues.LUMIASTREAM_DONATION, LumiaAlertValues.STREAMELEMENTS_DONATION],
4367
+ "cheer-latest": [LumiaAlertValues.YOUTUBE_SUPERCHAT]
4353
4368
  }
4354
4369
  };
4355
4370
  var CURATED_ALERTS = {
4356
4371
  twitch: [
4357
- "twitch-follower",
4358
- "twitch-subscriber",
4359
- "twitch-giftSubscription",
4360
- "twitch-bits",
4361
- "twitch-bitsCombo",
4362
- "twitch-raid",
4363
- "twitch-points",
4364
- "twitch-redemption",
4365
- "twitch-charityDonation",
4366
- "twitch-hypetrainStarted",
4367
- "twitch-clip",
4368
- "twitch-shoutoutReceive",
4369
- "twitch-watchStreak",
4370
- "twitch-firstChatter",
4371
- "twitch-extension"
4372
+ LumiaAlertValues.TWITCH_FOLLOWER,
4373
+ LumiaAlertValues.TWITCH_SUBSCRIBER,
4374
+ LumiaAlertValues.TWITCH_GIFT_SUBSCRIPTION,
4375
+ LumiaAlertValues.TWITCH_BITS,
4376
+ LumiaAlertValues.TWITCH_BITS_COMBO,
4377
+ LumiaAlertValues.TWITCH_RAID,
4378
+ LumiaAlertValues.TWITCH_POINTS,
4379
+ LumiaAlertValues.TWITCH_REDEMPTION,
4380
+ LumiaAlertValues.TWITCH_CHARITY_DONATION,
4381
+ LumiaAlertValues.TWITCH_HYPETRAIN_STARTED,
4382
+ LumiaAlertValues.TWITCH_CLIP,
4383
+ LumiaAlertValues.TWITCH_SHOUTOUT_RECEIVE,
4384
+ LumiaAlertValues.TWITCH_WATCH_STREAK,
4385
+ LumiaAlertValues.TWITCH_FIRST_CHATTER,
4386
+ LumiaAlertValues.TWITCH_EXTENSION
4372
4387
  ],
4373
4388
  kick: [
4374
- "kick-follower",
4375
- "kick-subscriber",
4376
- "kick-subscriptionGift",
4377
- "kick-kicks",
4378
- "kick-host",
4379
- "kick-points",
4380
- "kick-firstChatter"
4389
+ LumiaAlertValues.KICK_FOLLOWER,
4390
+ LumiaAlertValues.KICK_SUBSCRIBER,
4391
+ LumiaAlertValues.KICK_GIFT_SUBSCRIPTION,
4392
+ LumiaAlertValues.KICK_KICKS,
4393
+ LumiaAlertValues.KICK_HOST,
4394
+ LumiaAlertValues.KICK_POINTS,
4395
+ LumiaAlertValues.KICK_FIRST_CHATTER
4381
4396
  ],
4382
4397
  youtube: [
4383
- "youtube-subscriber",
4384
- "youtube-member",
4385
- "youtube-giftMembers",
4386
- "youtube-superchat",
4387
- "youtube-supersticker",
4388
- "youtube-gifts",
4389
- "youtube-like",
4390
- "youtube-firstChatter"
4398
+ LumiaAlertValues.YOUTUBE_SUBSCRIBER,
4399
+ LumiaAlertValues.YOUTUBE_MEMBER,
4400
+ LumiaAlertValues.YOUTUBE_GIFT_MEMBERS,
4401
+ LumiaAlertValues.YOUTUBE_SUPERCHAT,
4402
+ LumiaAlertValues.YOUTUBE_SUPERSTICKER,
4403
+ LumiaAlertValues.YOUTUBE_GIFTS,
4404
+ LumiaAlertValues.YOUTUBE_LIKE,
4405
+ LumiaAlertValues.YOUTUBE_FIRST_CHATTER
4391
4406
  ]
4392
4407
  };
4393
4408
  function curatedAlertsFor(provider = "twitch") {
@@ -4455,8 +4470,12 @@ function buildAlertEvent(seEvent, seEventType) {
4455
4470
  const shadowEnabled = text.enableShadow !== false;
4456
4471
  const importedTextShadow = css["text-shadow"] ?? "rgba(0, 0, 0, 0.8) 1px 1px 1px";
4457
4472
  const textShadow = shadowEnabled ? importedTextShadow : "none";
4458
- const marginTopRaw = Number(css["margin-top"]);
4459
- const pushTextUp = seLayoutUsesPushText(seEvent.layout) && Number.isFinite(marginTopRaw) ? -marginTopRaw : 0;
4473
+ const allowPush = seLayoutUsesPushText(seEvent.layout);
4474
+ const pushTextUp = allowPush ? pxToNumber(css["margin-top"], 0) : 0;
4475
+ const pushTextLeft = allowPush ? pxToNumber(css["margin-left"], 0) : 0;
4476
+ const usesColumnLayout = !seEvent.layout || seEvent.layout === "column";
4477
+ const usesRowLayout = seEvent.layout === "row";
4478
+ const tightLayout = allowPush && (usesColumnLayout || usesRowLayout);
4460
4479
  const variations = (seEvent.variations ?? []).map((variation) => buildAlertVariation(variation, seEventType, seEvent)).filter((v) => v != null);
4461
4480
  const defaultDuration = (seEventType && SE_DEFAULT_DURATION_BY_EVENT[seEventType]) ?? 8;
4462
4481
  return {
@@ -4483,7 +4502,7 @@ function buildAlertEvent(seEvent, seEventType) {
4483
4502
  showBrandIcon: true
4484
4503
  },
4485
4504
  settings: { duration: (seEvent.duration ?? defaultDuration) * 1e3 },
4486
- layout: { value: translateLayout(seEvent.layout), css: {}, customCss: "", pushTextUp, pushTextLeft: 0 },
4505
+ layout: { value: translateLayout(seEvent.layout), css: {}, customCss: "", pushTextUp, pushTextLeft, tight: tightLayout },
4487
4506
  text: {
4488
4507
  css: {
4489
4508
  fontFamily: css["font-family"] ?? "Roboto",
@@ -4492,6 +4511,10 @@ function buildAlertEvent(seEvent, seEventType) {
4492
4511
  fontWeight: css["font-weight"] ?? "bold",
4493
4512
  textShadow,
4494
4513
  textAlign: css["text-align"] ?? "center"
4514
+ // SE's per-event `margin-top` / `margin-left` are routed to
4515
+ // `layout.pushTextUp` / `layout.pushTextLeft` above (not
4516
+ // stamped on `text.css`) so they land in the alert editor's
4517
+ // dedicated sliders. See the long comment near `pushTextUp`.
4495
4518
  },
4496
4519
  messageCss,
4497
4520
  content: { value: translateSeText(text.message ?? "") }
@@ -4561,6 +4584,15 @@ function buildAlertVariation(v, seEventType, parentSettings) {
4561
4584
  const mergedSettings = deepMergeSeSettings(parentSettings ? parentInheritable : void 0, v.settings);
4562
4585
  const base = buildAlertEvent(mergedSettings, seEventType);
4563
4586
  return {
4587
+ // Stable per-variation ID. Lumia's alert editor (Settings.tsx) auto-
4588
+ // stamps a nanoid on first open for any variation missing an id, but
4589
+ // that leaves a window where the imported overlay's variations have
4590
+ // no stable key — list components rendering them key on
4591
+ // `variation.id || variation.name || idx` and adjacent variations
4592
+ // with the same name collide. Stamping at import time gives every
4593
+ // variation a stable identity from save #1 without waiting for the
4594
+ // editor's first mount.
4595
+ id: nanoid2(),
4564
4596
  name: v.name ?? "Variation",
4565
4597
  on: v.enabled !== false,
4566
4598
  randomChance: v.chance ?? 100,
@@ -4644,7 +4676,7 @@ function mapSingleAlert(widget, lumiaAlertKey, ctx) {
4644
4676
  css: { ...topCss, ...innerCss }
4645
4677
  };
4646
4678
  }
4647
- const built = buildAlertEvent(merged);
4679
+ const built = buildAlertEvent(merged, void 0);
4648
4680
  const events = {
4649
4681
  [lumiaAlertKey]: built
4650
4682
  };
@@ -4790,7 +4822,7 @@ function mapGoal(widget, ctx) {
4790
4822
  }
4791
4823
 
4792
4824
  // src/se-import/mappers/custom.ts
4793
- import { nanoid as nanoid2 } from "nanoid";
4825
+ import { nanoid as nanoid3 } from "nanoid";
4794
4826
  var SOURCE_TO_LUMIA_FIELD_TYPE = {
4795
4827
  text: "input",
4796
4828
  number: "number",
@@ -4880,7 +4912,7 @@ function mapCustom(widget, ctx) {
4880
4912
  // imports — codeId is the scoping key for variables, storage, and
4881
4913
  // event listeners. Mint a fresh nanoid so every imported widget
4882
4914
  // has its own scope.
4883
- codeId: nanoid2(),
4915
+ codeId: nanoid3(),
4884
4916
  html: v.html ?? "",
4885
4917
  css: v.css ?? "",
4886
4918
  js: v.js ?? "",
@@ -5017,6 +5049,63 @@ var TIPJAR_DEFAULT_MODULE = {
5017
5049
  version: 1,
5018
5050
  content: TIPJAR_DEFAULT_CONTENT_FIELDS
5019
5051
  };
5052
+ var EVENTLIST_DEFAULT_CONTENT_FIELDS = {
5053
+ version: 2,
5054
+ eventListType: {},
5055
+ theme: "basic",
5056
+ themeConfig: {},
5057
+ borderRadius: "10px",
5058
+ condensedText: true,
5059
+ src: "",
5060
+ // Category blacklist — events whose Lumia type appears here are
5061
+ // hidden. SL `show_<type>: false` maps to inserting the equivalent
5062
+ // Lumia category here.
5063
+ filters: [],
5064
+ sites: [],
5065
+ maxItemsToShow: 20,
5066
+ removeAfter: 0,
5067
+ reverse: false,
5068
+ horizontal: false,
5069
+ animations: {
5070
+ enterAnimation: "fadeIn",
5071
+ exitAnimation: "fadeOut",
5072
+ enterAnimationDuration: 1e3,
5073
+ exitAnimationDuration: 1e3,
5074
+ enterAnimationDelay: 0,
5075
+ exitAnimationDelay: 0
5076
+ },
5077
+ showDetailedText: true,
5078
+ hideAlertMessage: false,
5079
+ showAlertIcon: true,
5080
+ showSiteIcon: false,
5081
+ showUserAvatar: false,
5082
+ fadeOutAfterDelay: false,
5083
+ fadeOutDelayTime: 5,
5084
+ image: "",
5085
+ itemGap: 16,
5086
+ audio: {
5087
+ enter: "",
5088
+ volume: 1
5089
+ }
5090
+ };
5091
+ var EVENTLIST_DEFAULT_MODULE = {
5092
+ version: 2,
5093
+ content: EVENTLIST_DEFAULT_CONTENT_FIELDS,
5094
+ css: {
5095
+ borderRadius: "0px",
5096
+ borderStyle: "solid",
5097
+ borderWidth: "0px",
5098
+ borderColor: "transparent",
5099
+ fontSize: 16,
5100
+ lineHeight: 1.2,
5101
+ textAlign: "left",
5102
+ fontFamily: "Roboto",
5103
+ fontWeight: "normal",
5104
+ color: "#ffffff",
5105
+ textShadow: "",
5106
+ background: "transparent"
5107
+ }
5108
+ };
5020
5109
 
5021
5110
  // src/se-import/mappers/misc.ts
5022
5111
  function mapChatboxTheme(seTheme) {
@@ -5303,7 +5392,11 @@ function mapEventList(widget, ctx) {
5303
5392
  ...isMarquee ? { marqueeItemGap: 24 } : {},
5304
5393
  showAlertIcon: !isMarquee,
5305
5394
  showSiteIcon: !isMarquee,
5306
- showUserAvatar: true,
5395
+ // SE event lists are text-only (`{name}: {amount}` rows for tip-recent,
5396
+ // follower-recent, etc.) — no avatar column. Mirror that on import so
5397
+ // the streamer's layout matches what they had in SE; they can re-enable
5398
+ // avatars from the Eventlist settings panel if they want.
5399
+ showUserAvatar: false,
5307
5400
  showDetailedText: false,
5308
5401
  hideAlertMessage: false
5309
5402
  }
@@ -6966,11 +7059,6 @@ function rewriteAssetURLs(overlay, mapping) {
6966
7059
  };
6967
7060
  }
6968
7061
  var MAX_MIRROR_ASSET_BYTES = 100 * 1024 * 1024;
6969
- function looksLikeCorsError2(err) {
6970
- if (!(err instanceof TypeError)) return false;
6971
- const msg = (err.message ?? "").toLowerCase();
6972
- return msg.includes("failed to fetch") || msg.includes("networkerror") || msg.includes("network error") || msg.includes("cors") || msg.includes("access-control") || err.message.length > 0;
6973
- }
6974
7062
  async function mirrorOneAsset(url, upload, signal, proxyAssetFetch) {
6975
7063
  const blob = await fetchAssetBlob(url, signal, proxyAssetFetch);
6976
7064
  if (signal?.aborted) throw new DOMException("Aborted", "AbortError");
@@ -7002,7 +7090,7 @@ async function fetchAssetBlob(url, signal, proxyAssetFetch) {
7002
7090
  return res.blob();
7003
7091
  }
7004
7092
  if (signal?.aborted) throw new DOMException("Aborted", "AbortError");
7005
- if (proxyAssetFetch && looksLikeCorsError2(directErr)) {
7093
+ if (proxyAssetFetch && looksLikeCorsError(directErr)) {
7006
7094
  try {
7007
7095
  return await proxyAssetFetch(url);
7008
7096
  } catch (proxyErr) {
@@ -7053,7 +7141,7 @@ function guessMime(name) {
7053
7141
  }
7054
7142
 
7055
7143
  // src/se-import/transplant.ts
7056
- import { nanoid as nanoid3 } from "nanoid";
7144
+ import { nanoid as nanoid4 } from "nanoid";
7057
7145
  function cloneModuleForTransplant(srcModule, newId) {
7058
7146
  const clonedContent = srcModule.content ? structuredClone(srcModule.content) : srcModule.content;
7059
7147
  if (srcModule.settings?.type === "custom" && clonedContent) {
@@ -7071,7 +7159,7 @@ function transplantLayer(source, sourceLayerId) {
7071
7159
  const srcLayer = layers.find((l) => l.id === sourceLayerId);
7072
7160
  const srcModule = modules[sourceLayerId];
7073
7161
  if (!srcLayer || !srcModule) return null;
7074
- const rootNewId = nanoid3();
7162
+ const rootNewId = nanoid4();
7075
7163
  if (srcLayer.type !== "group") {
7076
7164
  const layer = { ...srcLayer, id: rootNewId };
7077
7165
  const module = cloneModuleForTransplant(srcModule, rootNewId);
@@ -7089,7 +7177,7 @@ function transplantLayer(source, sourceLayerId) {
7089
7177
  if (seen.has(child.id)) continue;
7090
7178
  seen.add(child.id);
7091
7179
  descendants.push(child);
7092
- idRemap.set(child.id, nanoid3());
7180
+ idRemap.set(child.id, nanoid4());
7093
7181
  if (child.type === "group") queue.push(child.id);
7094
7182
  }
7095
7183
  }
@@ -7593,7 +7681,74 @@ function readSLProvider(response) {
7593
7681
  }
7594
7682
 
7595
7683
  // src/sl-import/mappers/alert-box.ts
7596
- import { nanoid as nanoid4 } from "nanoid";
7684
+ import { nanoid as nanoid6 } from "nanoid";
7685
+ import { LumiaAlertValues as LumiaAlertValues2 } from "@lumiastream/lumia-types";
7686
+
7687
+ // src/se-import/import-envelope.ts
7688
+ import { nanoid as nanoid5 } from "nanoid";
7689
+ function buildModuleEnvelope(opts) {
7690
+ const id = opts.id ?? nanoid5();
7691
+ const locked = opts.locked ?? false;
7692
+ const visible = opts.visible ?? true;
7693
+ const layer = {
7694
+ id,
7695
+ ...opts.parentGroupId !== void 0 ? { group: opts.parentGroupId } : {},
7696
+ ...opts.type_layer ? { type: opts.type_layer } : {},
7697
+ state: {
7698
+ visible,
7699
+ locked,
7700
+ ...opts.expanded !== void 0 ? { expanded: opts.expanded } : {}
7701
+ },
7702
+ bounds: {
7703
+ x: opts.bounds.x,
7704
+ y: opts.bounds.y,
7705
+ width: opts.bounds.width,
7706
+ height: opts.bounds.height,
7707
+ scale: [1, 1],
7708
+ ...opts.zIndex !== void 0 ? { zIndex: opts.zIndex } : {}
7709
+ }
7710
+ };
7711
+ const module = {
7712
+ id,
7713
+ version: opts.version ?? 1,
7714
+ loaded: true,
7715
+ settings: { type: opts.type, title: opts.title, locked },
7716
+ lights: opts.lights ?? [],
7717
+ css: opts.css ?? {},
7718
+ content: opts.content ?? {},
7719
+ variables: opts.variables ?? {},
7720
+ events: opts.events ?? {},
7721
+ ...opts.alert !== void 0 ? { alert: opts.alert } : {}
7722
+ };
7723
+ return { id, layer, module };
7724
+ }
7725
+ var IMPORT_META_KEY = "importMeta";
7726
+ function stampImportMetaMerge(module, meta) {
7727
+ const existing = module.content?.[IMPORT_META_KEY] ?? {};
7728
+ module.content = {
7729
+ ...module.content ?? {},
7730
+ [IMPORT_META_KEY]: {
7731
+ importedAt: (/* @__PURE__ */ new Date()).toISOString(),
7732
+ ...existing,
7733
+ ...meta
7734
+ }
7735
+ };
7736
+ }
7737
+ function customHtmlReviewItem(moduleId, importSourceType, settings, reason) {
7738
+ return {
7739
+ moduleId,
7740
+ // ReviewItem's `seWidget` is typed against SEWidget but the review
7741
+ // UI just reads `.type` and `.settings`. Cast through unknown is
7742
+ // the only place we touch the shape mismatch; widening the
7743
+ // ReviewItem type is a future cleanup.
7744
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
7745
+ seWidget: { type: importSourceType, settings },
7746
+ status: "template",
7747
+ reason
7748
+ };
7749
+ }
7750
+
7751
+ // src/sl-import/mappers/alert-box.ts
7597
7752
  var SL_EVENT_ROUTING = {
7598
7753
  // Platform-native — provider routes (Twitch / Kick / YouTube).
7599
7754
  follow: { pickProviderKeys: "follower" },
@@ -7605,47 +7760,35 @@ var SL_EVENT_ROUTING = {
7605
7760
  // `subscriber` here is YouTube subscribe (free); `sponsor` is YouTube
7606
7761
  // channel membership; `fanfunding` is YouTube superchat. SL ships these
7607
7762
  // even on Twitch-primary accounts so we always map them.
7608
- subscriber: { keys: ["youtube-subscriber"] },
7609
- sponsor: { keys: ["youtube-member"] },
7610
- fanfunding: { keys: ["youtube-superchat"] },
7611
- super_sticker: { keys: ["youtube-supersticker"] },
7763
+ subscriber: { keys: [LumiaAlertValues2.YOUTUBE_SUBSCRIBER] },
7764
+ sponsor: { keys: [LumiaAlertValues2.YOUTUBE_MEMBER] },
7765
+ fanfunding: { keys: [LumiaAlertValues2.YOUTUBE_SUPERCHAT] },
7766
+ super_sticker: { keys: [LumiaAlertValues2.YOUTUBE_SUPERSTICKER] },
7612
7767
  // Donation / tip channels — Lumia ships dedicated alert keys for each
7613
7768
  // integration. `donation` itself fans out to `streamlabs-donation` AND
7614
7769
  // `lumiastream-donation` so the imported alert fires regardless of which
7615
7770
  // channel the donation actually came in on.
7616
- donation: { keys: ["streamlabs-donation", "lumiastream-donation"] },
7617
- eldonation: { keys: ["extralife-donation"] },
7618
- tiltifydonation: { keys: ["tiltify-campaignDonation"] },
7619
- donordrivedonation: { keys: ["donordrive-donation"] },
7620
- justgivingdonation: { keys: ["streamlabs-donation"] },
7621
- pledge: { keys: ["patreon-campaignPledge"] },
7771
+ donation: { keys: [LumiaAlertValues2.STREAMLABS_DONATION, LumiaAlertValues2.LUMIASTREAM_DONATION] },
7772
+ eldonation: { keys: [LumiaAlertValues2.EXTRALIFE_DONATION] },
7773
+ tiltifydonation: { keys: [LumiaAlertValues2.TILTIFY_DONATION] },
7774
+ donordrivedonation: { keys: [LumiaAlertValues2.DONORDRIVE_DONATION] },
7775
+ justgivingdonation: { keys: [LumiaAlertValues2.STREAMLABS_DONATION] },
7776
+ pledge: { keys: [LumiaAlertValues2.PATREON_PLEDGE] },
7622
7777
  // Charity / merch / loyalty / prime gifting — SL-flavored channels with
7623
7778
  // matching Lumia keys.
7624
- streamlabscharitydonation: { keys: ["streamlabs-charity"] },
7625
- twitchcharitydonation: { keys: ["twitch-charityDonation"] },
7626
- merch: { keys: ["streamlabs-merch"] },
7627
- loyalty_store_redemption: { keys: ["streamlabs-redemption"] },
7628
- prime_sub_gift: { keys: ["streamlabs-primegift"] },
7629
- treat: { keys: ["treatstream-treat"] },
7779
+ streamlabscharitydonation: { keys: [LumiaAlertValues2.STREAMLABS_CHARITY] },
7780
+ twitchcharitydonation: { keys: [LumiaAlertValues2.TWITCH_CHARITY_DONATION] },
7781
+ merch: { keys: [LumiaAlertValues2.STREAMLABS_MERCH] },
7782
+ loyalty_store_redemption: { keys: [LumiaAlertValues2.STREAMLABS_REDEMPTION] },
7783
+ prime_sub_gift: { keys: [LumiaAlertValues2.STREAMLABS_PRIMEGIFT] },
7784
+ treat: { keys: [LumiaAlertValues2.TREATSTREAM_TREAT] },
7630
7785
  // TikTok-flavored (jewel gift is TikTok-ish in concept).
7631
- jewel_gift: { keys: ["tiktok-gift"] }
7786
+ jewel_gift: { keys: [LumiaAlertValues2.TIKTOK_GIFT] }
7632
7787
  };
7633
7788
  function getField(settings, prefix, field) {
7634
7789
  const key = `${prefix}_${field}`;
7635
7790
  return settings[key];
7636
7791
  }
7637
- function pxToNumber(raw, fallback) {
7638
- if (typeof raw === "number" && Number.isFinite(raw)) return raw;
7639
- if (typeof raw !== "string") return fallback;
7640
- const trimmed = raw.trim();
7641
- if (!trimmed) return fallback;
7642
- if (trimmed.endsWith("px")) {
7643
- const n2 = parseFloat(trimmed.slice(0, -2));
7644
- return Number.isFinite(n2) ? n2 : fallback;
7645
- }
7646
- const n = parseFloat(trimmed);
7647
- return Number.isFinite(n) ? n : fallback;
7648
- }
7649
7792
  function translateLayout2(slLayout) {
7650
7793
  switch (slLayout) {
7651
7794
  case "above":
@@ -7755,9 +7898,7 @@ function buildBaseAlertEvent(settings, prefix, defaultMessage) {
7755
7898
  tts: {
7756
7899
  enabled: getField(settings, prefix, "tts_enabled") ?? false,
7757
7900
  voice: getField(settings, prefix, "tts_language") ?? "Brian",
7758
- volume: clampVolume(
7759
- getField(settings, prefix, "tts_volume") ?? 75
7760
- ),
7901
+ volume: clampVolume(getField(settings, prefix, "tts_volume") ?? 75),
7761
7902
  minAmount: getField(settings, prefix, "tts_min_amount") ?? 0
7762
7903
  },
7763
7904
  variations: []
@@ -7772,10 +7913,7 @@ function readSecondaryMessageConfig(settings, prefix, primaryFontFamily) {
7772
7913
  const enabled = settings[showKey];
7773
7914
  if (enabled !== true) return null;
7774
7915
  const fontFamily = getField(settings, secondaryPrefix, "message_font") ?? primaryFontFamily;
7775
- const fontSize = pxToNumber(
7776
- getField(settings, secondaryPrefix, "message_font_size"),
7777
- 20
7778
- );
7916
+ const fontSize = pxToNumber(getField(settings, secondaryPrefix, "message_font_size"), 20);
7779
7917
  const fontWeight = getField(settings, secondaryPrefix, "message_font_weight") ?? 400;
7780
7918
  const fontColor = getField(settings, secondaryPrefix, "message_font_color") ?? "#FFFFFF";
7781
7919
  return {
@@ -7802,11 +7940,20 @@ function translateSLVariationCondition(v) {
7802
7940
  const numericData = typeof data === "number" ? data : Number(data);
7803
7941
  switch (v.condition) {
7804
7942
  case "RANDOM":
7805
- return { conditionType: "RANDOM", condition: Number.isFinite(numericData) ? numericData : 100 };
7943
+ return {
7944
+ conditionType: "RANDOM",
7945
+ condition: Number.isFinite(numericData) ? numericData : 100
7946
+ };
7806
7947
  case "MIN_BITS_USED":
7807
- return { conditionType: "GREATER_NUMBER", condition: Number.isFinite(numericData) ? numericData : 0 };
7948
+ return {
7949
+ conditionType: "GREATER_NUMBER",
7950
+ condition: Number.isFinite(numericData) ? numericData : 0
7951
+ };
7808
7952
  case "EXACT_BITS_USED":
7809
- return { conditionType: "EQUAL_NUMBER", condition: Number.isFinite(numericData) ? numericData : 0 };
7953
+ return {
7954
+ conditionType: "EQUAL_NUMBER",
7955
+ condition: Number.isFinite(numericData) ? numericData : 0
7956
+ };
7810
7957
  case "MIN_MONTHS_SUBSCRIBED":
7811
7958
  return {
7812
7959
  conditionType: "SUBSCRIBED_MONTHS_GREATER",
@@ -7871,6 +8018,14 @@ function buildVariationEvent(v, parentBase) {
7871
8018
  if (s.hideAnimation) inherited.animations.alertExitAnimation = s.hideAnimation;
7872
8019
  if (s.layout) inherited.layout.value = translateLayout2(s.layout);
7873
8020
  return {
8021
+ // Reuse SL's own `uuid` when present so re-importing the same SL
8022
+ // alert-box keeps the variation identity stable across import
8023
+ // runs. SL always emits a uuid in their config (every fixture we've
8024
+ // seen has one); fall back to nanoid for the edge case of a
8025
+ // hand-crafted / older payload that omits it. Matches the SE
8026
+ // alert mapper's variation-id pattern — see Settings.tsx auto-fill
8027
+ // note there for why a stable id matters from save #1.
8028
+ id: typeof v.uuid === "string" && v.uuid.length > 0 ? v.uuid : nanoid6(),
7874
8029
  name: v.name ?? "Variation",
7875
8030
  on: v.paused !== true,
7876
8031
  randomChance: 100,
@@ -7936,11 +8091,7 @@ function buildSLAlertBox(settings, options = {}) {
7936
8091
  for (const [prefix, routing] of Object.entries(SL_EVENT_ROUTING)) {
7937
8092
  const lumiaKeys = routing.pickProviderKeys ? alertBoxKeysFor(routing.pickProviderKeys, provider) : routing.keys ?? [];
7938
8093
  if (lumiaKeys.length === 0) continue;
7939
- const built = buildBaseAlertEvent(
7940
- settings,
7941
- prefix,
7942
- DEFAULT_MESSAGES[prefix] ?? "{name}"
7943
- );
8094
+ const built = buildBaseAlertEvent(settings, prefix, DEFAULT_MESSAGES[prefix] ?? "{name}");
7944
8095
  if (!built) continue;
7945
8096
  const variationKey = VARIATION_KEY_FOR_PREFIX[prefix];
7946
8097
  const variations = variationKey ? settings[variationKey] : void 0;
@@ -7957,53 +8108,32 @@ function buildSLAlertBox(settings, options = {}) {
7957
8108
  channelsImported.push(prefix);
7958
8109
  if (firstEvent == null) firstEvent = built;
7959
8110
  }
7960
- const id = nanoid4();
7961
- const layer = {
7962
- id,
7963
- state: { visible: true, locked: false },
7964
- bounds: { x: 100, y: 100, width: 700, height: 600, scale: [1, 1] }
7965
- };
7966
8111
  const baseAlert = firstEvent ? stripEventOnlyFields(firstEvent) : {};
7967
- const module = {
7968
- id,
7969
- version: 1,
7970
- loaded: true,
7971
- settings: { type: "alert", title: "Streamlabs Alerts", locked: false },
7972
- lights: [],
7973
- css: {},
7974
- content: {},
7975
- variables: {},
7976
- events: {},
8112
+ const { layer, module } = buildModuleEnvelope({
8113
+ type: "alert",
8114
+ title: "Streamlabs Alerts",
8115
+ bounds: { x: 100, y: 100, width: 700, height: 600 },
7977
8116
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
7978
8117
  alert: { ...baseAlert, events }
7979
- };
8118
+ });
7980
8119
  return { layer, module, channelsImported };
7981
8120
  }
7982
8121
  function stripEventOnlyFields(event) {
7983
- const { on: _on, variations: _variations, matchEmptyCondition: _mec, ...base } = event;
8122
+ const {
8123
+ on: _on,
8124
+ variations: _variations,
8125
+ matchEmptyCondition: _mec,
8126
+ ...base
8127
+ } = event;
7984
8128
  return base;
7985
8129
  }
7986
8130
 
7987
8131
  // src/sl-import/mappers/tip-jar.ts
7988
- import { nanoid as nanoid5 } from "nanoid";
7989
- function pxToNumber2(raw, fallback) {
7990
- if (typeof raw === "number" && Number.isFinite(raw)) return raw;
7991
- if (typeof raw !== "string") return fallback;
7992
- const trimmed = raw.trim();
7993
- if (!trimmed) return fallback;
7994
- if (trimmed.endsWith("px")) {
7995
- const n2 = parseFloat(trimmed.slice(0, -2));
7996
- return Number.isFinite(n2) ? n2 : fallback;
7997
- }
7998
- const n = parseFloat(trimmed);
7999
- return Number.isFinite(n) ? n : fallback;
8000
- }
8001
8132
  function buildSLTipJar(settings) {
8002
- const id = nanoid5();
8003
8133
  const backgroundColor = settings.background?.color ?? "transparent";
8004
8134
  const textColor = settings.text?.color ?? "#FFFFFF";
8005
8135
  const fontFamily = settings.text?.font ?? "Open Sans";
8006
- const fontSize = pxToNumber2(settings.text?.size, 32);
8136
+ const fontSize = pxToNumber(settings.text?.size, 32);
8007
8137
  const types = settings.types ?? {};
8008
8138
  const tipsConfig = types.tips ?? {};
8009
8139
  const slEvents = {
@@ -8019,17 +8149,11 @@ function buildSLTipJar(settings) {
8019
8149
  ...TIPJAR_DEFAULT_MODULE.content.display,
8020
8150
  showEventMessages: settings.text?.show !== false
8021
8151
  };
8022
- const layer = {
8023
- id,
8024
- state: { visible: true, locked: false },
8025
- bounds: { x: 100, y: 100, width: 600, height: 700, scale: [1, 1] }
8026
- };
8027
- const module = {
8028
- id,
8152
+ const { layer, module } = buildModuleEnvelope({
8153
+ type: "tipjar",
8154
+ title: "Streamlabs Tip Jar",
8155
+ bounds: { x: 100, y: 100, width: 600, height: 700 },
8029
8156
  version: TIPJAR_DEFAULT_MODULE.version,
8030
- loaded: true,
8031
- settings: { type: "tipjar", title: "Streamlabs Tip Jar", locked: false },
8032
- lights: [],
8033
8157
  css: {
8034
8158
  fontFamily,
8035
8159
  fontSize,
@@ -8050,12 +8174,7 @@ function buildSLTipJar(settings) {
8050
8174
  source: "streamlabs",
8051
8175
  widgetType: "tip_jar",
8052
8176
  importedAt: (/* @__PURE__ */ new Date()).toISOString(),
8053
- // SL jar type doesn't have a 1:1 mapping to Lumia's preset
8054
- // vocabulary (cup-01, cup-02, …). Stashed for review.
8055
8177
  slJarType: settings.jar?.type ?? null,
8056
- // SL tier coin tiers — Lumia's sprite tiers are per-event and
8057
- // configured in the sprite editor; the user re-creates these
8058
- // manually. Stored here so they're not lost.
8059
8178
  slTipTiers: Array.isArray(tipsConfig.tiers) ? tipsConfig.tiers.map((t) => ({
8060
8179
  minimumAmount: t.minimum_amount,
8061
8180
  imageSrc: t.image_src ?? null
@@ -8063,15 +8182,12 @@ function buildSLTipJar(settings) {
8063
8182
  slMinimumTipAmount: tipsConfig.minimum_amount ?? null,
8064
8183
  slHadCustomHtml: settings.custom_html_enabled === true
8065
8184
  }
8066
- },
8067
- variables: {},
8068
- events: {}
8069
- };
8185
+ }
8186
+ });
8070
8187
  return { layer, module };
8071
8188
  }
8072
8189
 
8073
8190
  // src/sl-import/mappers/goal.ts
8074
- import { nanoid as nanoid6 } from "nanoid";
8075
8191
  var GOAL_SOURCE_MAP = {
8076
8192
  follower_goal: "follower",
8077
8193
  sub_goal: "subscriber",
@@ -8084,69 +8200,262 @@ var GOAL_TITLE_MAP = {
8084
8200
  donation_goal: "Donation Goal",
8085
8201
  bit_goal: "Bits Goal"
8086
8202
  };
8087
- function pxToNumber3(raw, fallback) {
8088
- if (typeof raw === "number" && Number.isFinite(raw)) return raw;
8089
- if (typeof raw !== "string") return fallback;
8090
- const trimmed = raw.trim();
8091
- if (!trimmed) return fallback;
8092
- if (trimmed.endsWith("px")) {
8093
- const n2 = parseFloat(trimmed.slice(0, -2));
8094
- return Number.isFinite(n2) ? n2 : fallback;
8095
- }
8096
- const n = parseFloat(trimmed);
8097
- return Number.isFinite(n) ? n : fallback;
8098
- }
8203
+ var GOAL_CURRENT_TEMPLATE = {
8204
+ follower_goal: "{{twitch_total_follower_count}}",
8205
+ sub_goal: "{{twitch_total_subscriber_count}}",
8206
+ donation_goal: "{{total_donation_amount}}",
8207
+ bit_goal: "{{twitch_total_bits_count}}"
8208
+ };
8099
8209
  function buildSLGoal(settings, widgetType) {
8100
- const id = nanoid6();
8101
8210
  const goalSource = GOAL_SOURCE_MAP[widgetType] ?? "follower";
8102
8211
  const defaultTitle = GOAL_TITLE_MAP[widgetType] ?? "Goal";
8212
+ const currentTemplate = GOAL_CURRENT_TEMPLATE[widgetType] ?? "";
8213
+ const requiresReview = settings.custom_enabled === true;
8103
8214
  const title = typeof settings.title === "string" ? settings.title : defaultTitle;
8104
8215
  const goalAmount = typeof settings.goal === "number" ? settings.goal : 100;
8105
8216
  const currentAmount = typeof settings.current === "number" ? settings.current : 0;
8106
8217
  const startingAmount = typeof settings.starting_amount === "number" ? settings.starting_amount : 0;
8107
- const endsAt = typeof settings.ends_at === "string" ? settings.ends_at : null;
8218
+ const endsAt = typeof settings.ends_at === "string" ? settings.ends_at : "";
8108
8219
  const barColor = settings.bar_color ?? "#32C3A6";
8109
- const barBackgroundColor = settings.bar_background_color ?? "#181818";
8220
+ const barBgColor = settings.bar_bg_color ?? "#181818";
8221
+ const widgetBgColor = settings.background_color ?? "transparent";
8110
8222
  const textColor = settings.text_color ?? "#FFFFFF";
8111
8223
  const barTextColor = settings.bar_text_color ?? textColor;
8112
8224
  const fontFamily = settings.font ?? "Open Sans";
8113
- const layer = {
8114
- id,
8115
- state: { visible: true, locked: false },
8116
- bounds: { x: 100, y: 100, width: 740, height: 90, scale: [1, 1] }
8117
- };
8118
- const module = {
8119
- id,
8120
- version: 1,
8121
- loaded: true,
8122
- settings: { type: "goal", title, locked: false },
8123
- lights: [],
8225
+ const fontSize = pxToNumber(settings.font_size, 24);
8226
+ const barThickness = pxToNumber(settings.bar_thickness, 48);
8227
+ const layerHeight = Math.max(60, barThickness + 32);
8228
+ const { layer, module } = buildModuleEnvelope({
8229
+ type: "goal",
8230
+ title,
8231
+ bounds: { x: 100, y: 100, width: 740, height: layerHeight },
8124
8232
  css: {
8125
8233
  fontFamily,
8126
8234
  color: textColor,
8127
- background: barBackgroundColor
8235
+ background: widgetBgColor,
8236
+ fontSize
8128
8237
  },
8238
+ // Use Lumia's canonical goal field names — `targetGoal` (string),
8239
+ // `current` (template string), `display`, `filledColor` /
8240
+ // `unfilledColor`. The prior schema used invented field names
8241
+ // (`goalAmount`, `barColor`) that the renderer ignored.
8129
8242
  content: {
8130
- title,
8131
- goalSource,
8132
- goalAmount,
8133
- currentAmount,
8134
- startingAmount,
8135
- endsAt,
8136
- barColor,
8137
- barBackgroundColor,
8138
- barTextColor,
8139
- fontFamily,
8140
- fontSize: pxToNumber3(settings.font_size, 24)
8243
+ version: 1,
8244
+ type: "bar",
8245
+ label: title,
8246
+ subLabel: "",
8247
+ display: "{{current}} / {{goal}}",
8248
+ current: currentAmount > 0 ? String(currentAmount) : currentTemplate,
8249
+ targetGoal: String(goalAmount),
8250
+ image: "",
8251
+ audio: {
8252
+ decrement: "",
8253
+ increment: "",
8254
+ goal: "",
8255
+ goalFailed: "",
8256
+ volume: 1
8257
+ },
8258
+ endDate: endsAt,
8259
+ goalAnimation: "",
8260
+ goalAnimationLoop: true,
8261
+ unfilledColor: barBgColor,
8262
+ filledColor: barColor,
8263
+ alignItemsToEdges: false,
8264
+ border: "solid 1px transparent",
8265
+ borderRadius: "40px",
8266
+ highlightColor: "inherit",
8267
+ importMeta: {
8268
+ source: "streamlabs",
8269
+ widgetType,
8270
+ importedAt: (/* @__PURE__ */ new Date()).toISOString(),
8271
+ goalSource,
8272
+ slLayout: settings.layout ?? null,
8273
+ slBarTextColor: barTextColor,
8274
+ slBarThickness: barThickness,
8275
+ slStartingAmount: startingAmount,
8276
+ slHadCustomHtml: settings.custom_enabled === true
8277
+ }
8278
+ }
8279
+ });
8280
+ return { layer, module, requiresReview };
8281
+ }
8282
+
8283
+ // src/sl-import/mappers/chat-box.ts
8284
+ function buildSitesArray(settings) {
8285
+ const defaultSites = [...CHATBOX_DEFAULT_MODULE.content.sites];
8286
+ const drop = /* @__PURE__ */ new Set();
8287
+ if (settings.show_bttv_emotes === false) drop.add("bttv");
8288
+ if (settings.show_franker_emotes === false) drop.add("ffz");
8289
+ if (settings.show_7tv_emotes === false) drop.add("seventv");
8290
+ if (settings.twitch_enabled === false) drop.add("twitch");
8291
+ if (settings.youtube_enabled === false) drop.add("youtube");
8292
+ if (settings.facebook_enabled === false) drop.add("facebook");
8293
+ if (settings.tiktok_enabled === false) drop.add("tiktok");
8294
+ if (settings.kick_enabled === false) drop.add("kick");
8295
+ if (drop.size === 0) return defaultSites;
8296
+ return defaultSites.filter((s) => !drop.has(s));
8297
+ }
8298
+ function buildSLChatBox(settings) {
8299
+ const requiresReview = settings.custom_enabled === true;
8300
+ const backgroundColor = settings.background_color ?? "transparent";
8301
+ const textColor = settings.text_color ?? "#FFFFFF";
8302
+ const textSize = pxToNumber(settings.text_size, 16);
8303
+ const fadeOutAfterDelay = settings.always_show_messages !== true;
8304
+ const fadeOutDelayTime = fadeOutAfterDelay ? Math.max(1, Math.round((settings.message_hide_delay ?? 15e3) / 1e3)) : 0;
8305
+ const sites = buildSitesArray(settings);
8306
+ const { layer, module } = buildModuleEnvelope({
8307
+ type: "chatbox",
8308
+ title: "Streamlabs Chat Box",
8309
+ bounds: { x: 100, y: 100, width: 375, height: 470 },
8310
+ version: CHATBOX_DEFAULT_MODULE.version,
8311
+ css: {
8312
+ background: backgroundColor,
8313
+ color: textColor,
8314
+ fontSize: textSize
8141
8315
  },
8142
- variables: {},
8143
- events: {}
8144
- };
8145
- return { layer, module };
8316
+ content: {
8317
+ // Spread canonical defaults FIRST so the renderer's dereferences
8318
+ // (chatboxStreamingSite.<platform>.themeConfig, animations,
8319
+ // chatboxStreamingSite.<platform>.message, etc.) all exist.
8320
+ ...CHATBOX_DEFAULT_MODULE.content,
8321
+ // Then override the bits SL controls.
8322
+ sites,
8323
+ fadeOutAfterDelay,
8324
+ fadeOutDelayTime,
8325
+ // SL keeps history forever by default — `always_show_messages`
8326
+ // implies the streamer wants a permanent scrollback. Lumia
8327
+ // exposes that as `permanent: true`.
8328
+ permanent: settings.always_show_messages === true,
8329
+ // Preserve the SL-specific provenance for the review step.
8330
+ importMeta: {
8331
+ source: "streamlabs",
8332
+ widgetType: "chat_box",
8333
+ importedAt: (/* @__PURE__ */ new Date()).toISOString(),
8334
+ slTheme: settings.theme ?? null,
8335
+ slHadCustomHtml: settings.custom_enabled === true,
8336
+ slHidesCommonBots: settings.hide_common_chat_bots === true,
8337
+ slHidesCommands: settings.hide_commands === true,
8338
+ slMutedChatters: settings.muted_chatters ?? "",
8339
+ slMessageShowDelayMs: settings.message_show_delay ?? 0
8340
+ }
8341
+ }
8342
+ });
8343
+ return { layer, module, requiresReview };
8344
+ }
8345
+
8346
+ // src/sl-import/mappers/event-list.ts
8347
+ var SL_SHOW_FLAG_TO_LUMIA_FILTER = {
8348
+ show_follows: "follower",
8349
+ show_subscriptions: "subscribers",
8350
+ show_resubs: "subscribers",
8351
+ show_sub_gifts: "gifts",
8352
+ show_subscribers: "subscribers",
8353
+ show_sponsors: "subscribers",
8354
+ // YouTube channel members
8355
+ show_sponsor_gifts: "gifts",
8356
+ show_bits: "bits",
8357
+ show_donations: "donation",
8358
+ show_pledges: "donation",
8359
+ // Patreon
8360
+ show_eldonations: "donation",
8361
+ show_tiltifydonations: "donation",
8362
+ show_donordrivedonations: "donation",
8363
+ show_justgivingdonations: "donation",
8364
+ show_streamlabscharitydonations: "donation",
8365
+ show_twitchcharitydonations: "donation",
8366
+ show_twitch_charity: "donation",
8367
+ show_treats: "donation",
8368
+ // TreatStream
8369
+ show_merch: "purchases",
8370
+ show_raids: "raids",
8371
+ show_hosts: "raids",
8372
+ show_redemptions: "redemption",
8373
+ show_fanfundings: "superchats",
8374
+ // YouTube Super Chat
8375
+ show_super_stickers: "superstickers",
8376
+ show_jewel_gifts: "stars"
8377
+ // TikTok-flavored, closest analogue
8378
+ };
8379
+ function buildFilters(settings) {
8380
+ const drop = /* @__PURE__ */ new Set();
8381
+ for (const [slFlag, lumiaCategory] of Object.entries(SL_SHOW_FLAG_TO_LUMIA_FILTER)) {
8382
+ if (settings[slFlag] === false) {
8383
+ drop.add(lumiaCategory);
8384
+ }
8385
+ }
8386
+ return Array.from(drop);
8387
+ }
8388
+ function buildSLEventList(settings) {
8389
+ const requiresReview = settings.custom_enabled === true;
8390
+ const backgroundColor = settings.background_color ?? "transparent";
8391
+ const textColor = settings.text_color ?? "#FFFFFF";
8392
+ const textSize = pxToNumber(settings.text_size, 16);
8393
+ const fontFamily = settings.font_family ?? "Roboto";
8394
+ const maxEvents = typeof settings.max_events === "number" && settings.max_events > 0 ? settings.max_events : EVENTLIST_DEFAULT_MODULE.content.maxItemsToShow;
8395
+ const enterAnimation = settings.show_animation ?? "fadeIn";
8396
+ const exitAnimation = settings.hide_animation ?? "fadeOut";
8397
+ const animationDuration = typeof settings.animation_speed === "number" ? settings.animation_speed : 1e3;
8398
+ const filters = buildFilters(settings);
8399
+ const { layer, module } = buildModuleEnvelope({
8400
+ type: "eventlist",
8401
+ title: "Streamlabs Event List",
8402
+ bounds: { x: 100, y: 100, width: 375, height: 470 },
8403
+ version: EVENTLIST_DEFAULT_MODULE.version,
8404
+ css: {
8405
+ ...EVENTLIST_DEFAULT_MODULE.css,
8406
+ background: backgroundColor,
8407
+ color: textColor,
8408
+ fontSize: textSize,
8409
+ fontFamily
8410
+ },
8411
+ content: {
8412
+ // Spread canonical defaults FIRST so the renderer's dereferences
8413
+ // don't crash on missing nested objects.
8414
+ ...EVENTLIST_DEFAULT_MODULE.content,
8415
+ maxItemsToShow: maxEvents,
8416
+ // SL's `flip_y` corresponds to Lumia's `reverse` (newest item
8417
+ // at the bottom vs the top). SL's `flip_x` flips the alignment
8418
+ // to right; Lumia covers that via css.textAlign but doesn't
8419
+ // have a per-module horizontal flip — stash as importMeta for
8420
+ // the review step.
8421
+ reverse: settings.flip_y === true,
8422
+ // SL's `keep_history: false` means events disappear after the
8423
+ // fade time; map to `fadeOutAfterDelay: true` with the fade
8424
+ // time SL configured.
8425
+ fadeOutAfterDelay: settings.keep_history === false,
8426
+ fadeOutDelayTime: typeof settings.fade_time === "number" && settings.fade_time > 0 ? Math.max(1, Math.round(settings.fade_time / 1e3)) : EVENTLIST_DEFAULT_MODULE.content.fadeOutDelayTime,
8427
+ animations: {
8428
+ ...EVENTLIST_DEFAULT_MODULE.content.animations,
8429
+ enterAnimation,
8430
+ exitAnimation,
8431
+ enterAnimationDuration: animationDuration,
8432
+ exitAnimationDuration: animationDuration
8433
+ },
8434
+ filters,
8435
+ importMeta: {
8436
+ source: "streamlabs",
8437
+ widgetType: "event_list",
8438
+ importedAt: (/* @__PURE__ */ new Date()).toISOString(),
8439
+ slTheme: settings.theme ?? null,
8440
+ slThemeColor: settings.theme_color ?? null,
8441
+ slFlipX: settings.flip_x === true,
8442
+ slHadCustomHtml: settings.custom_enabled === true,
8443
+ // Per-event-type minimums SL filters by — Lumia's eventlist
8444
+ // has no equivalent thresholding; preserve so the streamer
8445
+ // can recreate via the alert module's variation conditions.
8446
+ slHostViewerMinimum: settings.host_viewer_minimum ?? 0,
8447
+ slBitsMinimum: settings.bits_minimum ?? 0,
8448
+ slRaidRaiderMinimum: settings.raid_raider_minimum ?? 0
8449
+ }
8450
+ }
8451
+ });
8452
+ return { layer, module, requiresReview };
8146
8453
  }
8147
8454
 
8148
8455
  // src/sl-import/sl-dispatch.ts
8149
- var IMPORT_META_KEY = "importMeta";
8456
+ function buildSLDescription(widgetType, sourceUrl) {
8457
+ return buildImportDescription(`Imported from Streamlabs ${widgetType}`, sourceUrl);
8458
+ }
8150
8459
  var GOAL_WIDGET_TYPES = /* @__PURE__ */ new Set([
8151
8460
  "follower_goal",
8152
8461
  "sub_goal",
@@ -8154,8 +8463,6 @@ var GOAL_WIDGET_TYPES = /* @__PURE__ */ new Set([
8154
8463
  "bit_goal"
8155
8464
  ]);
8156
8465
  var PLACEHOLDER_WIDGET_TYPES = /* @__PURE__ */ new Set([
8157
- "chat_box",
8158
- "event_list",
8159
8466
  "donation_ticker",
8160
8467
  "stream_boss",
8161
8468
  "credits",
@@ -8165,8 +8472,6 @@ var PLACEHOLDER_WIDGET_TYPES = /* @__PURE__ */ new Set([
8165
8472
  "streamlabels"
8166
8473
  ]);
8167
8474
  var PLACEHOLDER_REASONS = {
8168
- chat_box: "Lumia has a native chatbox module \u2014 set it up directly rather than importing the SL config.",
8169
- event_list: "Lumia has a native eventlist module \u2014 set it up directly rather than importing the SL config.",
8170
8475
  donation_ticker: "No native Lumia equivalent yet \u2014 imported as a placeholder text layer.",
8171
8476
  stream_boss: "Lumia has a native streamboss module \u2014 set it up directly rather than importing the SL config.",
8172
8477
  credits: "Lumia has a native credits module \u2014 set it up directly rather than importing the SL config.",
@@ -8175,19 +8480,14 @@ var PLACEHOLDER_REASONS = {
8175
8480
  sub_train: "No native Lumia equivalent yet \u2014 imported as a placeholder text layer.",
8176
8481
  streamlabels: "Use Lumia template variables (e.g. {{twitch_last_follower}}) inside a Text module instead of importing the SL streamlabels config."
8177
8482
  };
8483
+ var CUSTOM_HTML_REVIEW_REASON = "The Streamlabs widget used custom HTML/CSS/JS that Lumia cannot host. The native Lumia module is the right starting point \u2014 recreate the styling there. The original SL settings are stashed under module.content.importMeta for reference.";
8178
8484
  function stampImportMeta(module, widgetType, role, extra = {}) {
8179
- const existing = module.content?.[IMPORT_META_KEY] ?? {};
8180
- module.content = {
8181
- ...module.content ?? {},
8182
- [IMPORT_META_KEY]: {
8183
- source: "streamlabs",
8184
- widgetType,
8185
- role,
8186
- importedAt: (/* @__PURE__ */ new Date()).toISOString(),
8187
- ...existing,
8188
- ...extra
8189
- }
8190
- };
8485
+ stampImportMetaMerge(module, {
8486
+ source: "streamlabs",
8487
+ widgetType,
8488
+ role,
8489
+ ...extra
8490
+ });
8191
8491
  }
8192
8492
  function buildPlaceholder(widgetType) {
8193
8493
  const id = nanoid7();
@@ -8219,7 +8519,7 @@ function buildPlaceholder(widgetType) {
8219
8519
  };
8220
8520
  return { layer, module };
8221
8521
  }
8222
- function importStreamlabsWidget(response, widgetType) {
8522
+ function importStreamlabsWidget(response, widgetType, options = {}) {
8223
8523
  if (!isSLWidgetConfigResponse(response)) {
8224
8524
  throw new Error(
8225
8525
  "Streamlabs response is missing the expected `settings` envelope. The widget may be private or the response format may have changed."
@@ -8269,9 +8569,61 @@ function importStreamlabsWidget(response, widgetType) {
8269
8569
  coverage.mappings.push({
8270
8570
  seType: `streamlabs:${widgetType}`,
8271
8571
  lumiaType: "goal",
8272
- status: "direct",
8572
+ status: built.requiresReview ? "partial" : "direct",
8573
+ count: 1
8574
+ });
8575
+ if (built.requiresReview) {
8576
+ reviewItems.push(
8577
+ customHtmlReviewItem(
8578
+ built.module.id,
8579
+ `streamlabs-${widgetType}`,
8580
+ response.settings,
8581
+ CUSTOM_HTML_REVIEW_REASON
8582
+ )
8583
+ );
8584
+ }
8585
+ } else if (widgetType === "chat_box") {
8586
+ const built = buildSLChatBox(response.settings);
8587
+ stampImportMeta(built.module, widgetType, "chatbox");
8588
+ layers.push(built.layer);
8589
+ modules[built.layer.id] = built.module;
8590
+ coverage.mappings.push({
8591
+ seType: "streamlabs:chat_box",
8592
+ lumiaType: "chatbox",
8593
+ status: built.requiresReview ? "partial" : "direct",
8594
+ count: 1
8595
+ });
8596
+ if (built.requiresReview) {
8597
+ reviewItems.push(
8598
+ customHtmlReviewItem(
8599
+ built.module.id,
8600
+ "streamlabs-chat_box",
8601
+ response.settings,
8602
+ CUSTOM_HTML_REVIEW_REASON
8603
+ )
8604
+ );
8605
+ }
8606
+ } else if (widgetType === "event_list") {
8607
+ const built = buildSLEventList(response.settings);
8608
+ stampImportMeta(built.module, widgetType, "eventlist");
8609
+ layers.push(built.layer);
8610
+ modules[built.layer.id] = built.module;
8611
+ coverage.mappings.push({
8612
+ seType: "streamlabs:event_list",
8613
+ lumiaType: "eventlist",
8614
+ status: built.requiresReview ? "partial" : "direct",
8273
8615
  count: 1
8274
8616
  });
8617
+ if (built.requiresReview) {
8618
+ reviewItems.push(
8619
+ customHtmlReviewItem(
8620
+ built.module.id,
8621
+ "streamlabs-event_list",
8622
+ response.settings,
8623
+ CUSTOM_HTML_REVIEW_REASON
8624
+ )
8625
+ );
8626
+ }
8275
8627
  } else if (PLACEHOLDER_WIDGET_TYPES.has(widgetType)) {
8276
8628
  const placeholder = buildPlaceholder(widgetType);
8277
8629
  const reason = PLACEHOLDER_REASONS[widgetType] ?? "No native Lumia mapping yet.";
@@ -8320,7 +8672,7 @@ function importStreamlabsWidget(response, widgetType) {
8320
8672
  uuid: "",
8321
8673
  listen_id: "",
8322
8674
  name: overlayName,
8323
- description: `Imported from Streamlabs ${widgetType}`,
8675
+ description: buildSLDescription(widgetType, options.sourceUrl),
8324
8676
  settings: {
8325
8677
  metadata: { width: 1920, height: 1080 },
8326
8678
  layers,
@@ -9882,7 +10234,12 @@ function SEImportWizard({
9882
10234
  );
9883
10235
  }
9884
10236
  importedOverlays.push({
9885
- result: importSEBootstrap(bootstrap),
10237
+ result: importSEBootstrap(bootstrap, {
10238
+ // JWT-flow imports don't come from a pasted URL; synthesize
10239
+ // the SE dashboard URL so the description still carries a
10240
+ // useful traceback. Same shape SE editor links use.
10241
+ sourceUrl: `https://streamelements.com/dashboard/overlays/${overlayId}/editor`
10242
+ }),
9886
10243
  summary
9887
10244
  });
9888
10245
  }
@@ -9927,7 +10284,9 @@ function SEImportWizard({
9927
10284
  "StreamElements returned a payload that doesn't look like an overlay bootstrap."
9928
10285
  );
9929
10286
  }
9930
- const imported = importSEBootstrap(bootstrap);
10287
+ const imported = importSEBootstrap(bootstrap, {
10288
+ sourceUrl: `https://streamelements.com/dashboard/overlays/${overlayId}/editor`
10289
+ });
9931
10290
  setBatchImports([{ result: imported, summary }]);
9932
10291
  setResult(imported);
9933
10292
  setAssetUrls(findSEAssetURLs(imported.overlay));
@@ -10015,7 +10374,9 @@ function SEImportWizard({
10015
10374
  "Streamlabs returned a response that does not look like a widget config payload."
10016
10375
  );
10017
10376
  }
10018
- const imported = importStreamlabsWidget(raw, slParts.widgetType);
10377
+ const imported = importStreamlabsWidget(raw, slParts.widgetType, {
10378
+ sourceUrl: url
10379
+ });
10019
10380
  setBatchImports([]);
10020
10381
  setResult(imported);
10021
10382
  setAssetUrls(findSEAssetURLs(imported.overlay));
@@ -10056,7 +10417,7 @@ function SEImportWizard({
10056
10417
  "StreamElements returned a response that does not look like an Element widget payload."
10057
10418
  );
10058
10419
  }
10059
- const imported = importElementOverlay(raw);
10420
+ const imported = importElementOverlay(raw, { sourceUrl: url });
10060
10421
  setBatchImports([]);
10061
10422
  setResult(imported);
10062
10423
  setAssetUrls(findSEAssetURLs(imported.overlay));
@@ -10104,7 +10465,7 @@ function SEImportWizard({
10104
10465
  ...parts,
10105
10466
  proxyFetch: bindings.proxyFetch
10106
10467
  });
10107
- const imported = importSEBootstrap(bootstrap);
10468
+ const imported = importSEBootstrap(bootstrap, { sourceUrl: url });
10108
10469
  setBatchImports([]);
10109
10470
  setResult(imported);
10110
10471
  setAssetUrls(findSEAssetURLs(imported.overlay));
@@ -10763,7 +11124,7 @@ function extractErrorMessage(err) {
10763
11124
  }
10764
11125
 
10765
11126
  // src/se-import/element-dispatch.ts
10766
- import { nanoid as nanoid9 } from "nanoid";
11127
+ import { LumiaAlertValues as LumiaAlertValues3 } from "@lumiastream/lumia-types";
10767
11128
 
10768
11129
  // src/se-import/element-types.ts
10769
11130
  function isSEElementResponse(value) {
@@ -11007,34 +11368,8 @@ function translateAnimationName(name, fallback) {
11007
11368
  if (PINNED_ANIMATION_MAP[name]) return PINNED_ANIMATION_MAP[name];
11008
11369
  return name.replace(/-([a-z])/g, (_m, c) => c.toUpperCase());
11009
11370
  }
11010
- function parseAnimationDurationMs(raw, fallbackMs) {
11011
- if (typeof raw === "number" && Number.isFinite(raw)) return raw;
11012
- if (typeof raw !== "string") return fallbackMs;
11013
- const trimmed = raw.trim();
11014
- if (!trimmed) return fallbackMs;
11015
- if (trimmed.endsWith("ms")) {
11016
- const n2 = parseFloat(trimmed.slice(0, -2));
11017
- return Number.isFinite(n2) ? n2 : fallbackMs;
11018
- }
11019
- if (trimmed.endsWith("s")) {
11020
- const n2 = parseFloat(trimmed.slice(0, -1));
11021
- return Number.isFinite(n2) ? n2 * 1e3 : fallbackMs;
11022
- }
11023
- const n = parseFloat(trimmed);
11024
- return Number.isFinite(n) ? n : fallbackMs;
11025
- }
11026
- function parsePx2(raw) {
11027
- if (typeof raw === "number" && Number.isFinite(raw)) return raw;
11028
- if (typeof raw !== "string") return NaN;
11029
- const trimmed = raw.trim();
11030
- if (!trimmed) return NaN;
11031
- if (trimmed.endsWith("px")) {
11032
- const n2 = parseFloat(trimmed.slice(0, -2));
11033
- return Number.isFinite(n2) ? n2 : NaN;
11034
- }
11035
- const n = parseFloat(trimmed);
11036
- return Number.isFinite(n) ? n : NaN;
11037
- }
11371
+ var parseAnimationDurationMs = parseDurationMs;
11372
+ var parsePx2 = (raw) => pxToNumber(raw, NaN);
11038
11373
  function findFieldByType(fields, type) {
11039
11374
  if (!fields) return void 0;
11040
11375
  for (const f of Object.values(fields)) {
@@ -11110,6 +11445,47 @@ function buildAnimations(title) {
11110
11445
  const visibleDurationMs = enterDelay + enterDuration + staticDuration + exitDuration;
11111
11446
  return { animations, visibleDurationMs: visibleDurationMs || DEFAULT_DURATION_MS };
11112
11447
  }
11448
+ function resolveFieldBoundsInCanvas(style, canvas, textContent) {
11449
+ if (!style) return null;
11450
+ const x = parsePx2(style.left);
11451
+ const y = parsePx2(style.top);
11452
+ const hasXY = Number.isFinite(x) || Number.isFinite(y);
11453
+ if (!hasXY && style.width == null && style.height == null) return null;
11454
+ const sx = Number.isFinite(x) ? x : 0;
11455
+ const sy = Number.isFinite(y) ? y : 0;
11456
+ let width = parsePx2(style.width);
11457
+ let height = parsePx2(style.height);
11458
+ if (!Number.isFinite(width)) width = Math.max(1, canvas.width - sx);
11459
+ if (!Number.isFinite(height)) {
11460
+ if (typeof textContent === "string") {
11461
+ const lineCount = (textContent.match(/\n/g)?.length ?? 0) + 1;
11462
+ const lhPx = parsePx2(style.lineHeight) || parsePx2(style["line-height"]);
11463
+ const fsPx = parsePx2(style.fontSize);
11464
+ let effectiveLh = lhPx;
11465
+ if (!Number.isFinite(effectiveLh) || effectiveLh < 8) {
11466
+ effectiveLh = Number.isFinite(fsPx) ? fsPx * 1.3 : 24 * 1.3;
11467
+ }
11468
+ height = Math.max(1, lineCount * effectiveLh);
11469
+ } else {
11470
+ height = Math.max(1, canvas.height - sy);
11471
+ }
11472
+ }
11473
+ return { x: sx, y: sy, width, height };
11474
+ }
11475
+ function unionBounds(boxes) {
11476
+ if (boxes.length === 0) return null;
11477
+ let minX = Infinity;
11478
+ let minY = Infinity;
11479
+ let maxX = -Infinity;
11480
+ let maxY = -Infinity;
11481
+ for (const b of boxes) {
11482
+ minX = Math.min(minX, b.x);
11483
+ minY = Math.min(minY, b.y);
11484
+ maxX = Math.max(maxX, b.x + b.width);
11485
+ maxY = Math.max(maxY, b.y + b.height);
11486
+ }
11487
+ return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
11488
+ }
11113
11489
  function buildElementAlertEvent(state, options = {}) {
11114
11490
  const compositeFields = state.settings?.compositeFields;
11115
11491
  const titleField = findFieldByType(compositeFields, "text");
@@ -11127,6 +11503,31 @@ function buildElementAlertEvent(state, options = {}) {
11127
11503
  const titleStyle = titleField?.style;
11128
11504
  const highlightColor = titleField?.highlightedStyle?.color ?? null;
11129
11505
  const { animations, visibleDurationMs } = buildAnimations(titleField);
11506
+ const canvas = options.canvas ?? { width: 1920, height: 1080 };
11507
+ const mediaBounds = resolveFieldBoundsInCanvas(mainAsset?.style, canvas);
11508
+ const textBounds = resolveFieldBoundsInCanvas(titleStyle, canvas, rawMessage);
11509
+ const collected = [];
11510
+ if (mediaBounds) collected.push(mediaBounds);
11511
+ if (textBounds) collected.push(textBounds);
11512
+ let layerBounds = unionBounds(collected);
11513
+ if (!layerBounds) {
11514
+ layerBounds = {
11515
+ x: Math.round(canvas.width * 0.6),
11516
+ y: Math.round(canvas.height * 0.15),
11517
+ width: 400,
11518
+ height: 300
11519
+ };
11520
+ }
11521
+ let pushTextUp = 0;
11522
+ let pushTextLeft = 0;
11523
+ if (textBounds) {
11524
+ const desiredTextTopInLayer = textBounds.y - layerBounds.y;
11525
+ const naturalTextTopInLayer = Math.max(0, (layerBounds.height - textBounds.height) / 2);
11526
+ pushTextUp = Math.round(desiredTextTopInLayer - naturalTextTopInLayer);
11527
+ const desiredTextLeftInLayer = textBounds.x - layerBounds.x;
11528
+ const naturalTextLeftInLayer = Math.max(0, (layerBounds.width - textBounds.width) / 2);
11529
+ pushTextLeft = Math.round(desiredTextLeftInLayer - naturalTextLeftInLayer);
11530
+ }
11130
11531
  const event = {
11131
11532
  on: true,
11132
11533
  // `'custom'` lowercase is load-bearing in Lumia's AlertBoxHandler —
@@ -11145,7 +11546,11 @@ function buildElementAlertEvent(state, options = {}) {
11145
11546
  showBrandIcon: true
11146
11547
  },
11147
11548
  settings: { duration: visibleDurationMs },
11148
- layout: { value: "column", css: {}, customCss: "", pushTextUp: 0, pushTextLeft: 0 },
11549
+ // `textOver` matches SE Element's text-on-video render pattern.
11550
+ // `imageOver` / `column` / `row` would all stack the text outside
11551
+ // the media, which is the bug this layout fixes (text rendered
11552
+ // below the alert graphic instead of overlaid on it).
11553
+ layout: { value: "textOver", css: {}, customCss: "", pushTextUp, pushTextLeft },
11149
11554
  text: {
11150
11555
  css: titleCss(titleStyle),
11151
11556
  messageCss: {},
@@ -11167,44 +11572,13 @@ function buildElementAlertEvent(state, options = {}) {
11167
11572
  },
11168
11573
  variations: []
11169
11574
  };
11170
- const style = mainAsset?.style;
11171
- let bounds;
11172
- if (style) {
11173
- const x = parsePx2(style.left);
11174
- const y = parsePx2(style.top);
11175
- const width = parsePx2(style.width);
11176
- const height = parsePx2(style.height);
11177
- if (Number.isFinite(x) && Number.isFinite(y) && Number.isFinite(width)) {
11178
- bounds = {
11179
- x,
11180
- y,
11181
- width,
11182
- // Height isn't always present on video Main Assets (the video
11183
- // aspect-ratios itself from the source). Fall back to a
11184
- // reasonable default that matches SE's preview row.
11185
- height: Number.isFinite(height) ? height : Math.round(width * 0.75)
11186
- };
11187
- }
11188
- }
11189
- return { event, bounds };
11575
+ return { event, bounds: layerBounds };
11190
11576
  }
11191
11577
 
11192
11578
  // src/se-import/mappers/element-scene.ts
11193
- import { nanoid as nanoid8 } from "nanoid";
11194
11579
  var TEXT_FALLBACK = { width: 400, height: 50 };
11195
11580
  var IMAGE_FALLBACK = { width: 300, height: 100 };
11196
- function parsePx3(raw) {
11197
- if (typeof raw === "number" && Number.isFinite(raw)) return raw;
11198
- if (typeof raw !== "string") return NaN;
11199
- const trimmed = raw.trim();
11200
- if (!trimmed) return NaN;
11201
- if (trimmed.endsWith("px")) {
11202
- const n2 = parseFloat(trimmed.slice(0, -2));
11203
- return Number.isFinite(n2) ? n2 : NaN;
11204
- }
11205
- const n = parseFloat(trimmed);
11206
- return Number.isFinite(n) ? n : NaN;
11207
- }
11581
+ var parsePx3 = (raw) => pxToNumber(raw, NaN);
11208
11582
  function styleToBounds(style, type, canvas) {
11209
11583
  const x = Number.isFinite(parsePx3(style?.left)) ? parsePx3(style?.left) : 0;
11210
11584
  const y = Number.isFinite(parsePx3(style?.top)) ? parsePx3(style?.top) : 0;
@@ -11240,33 +11614,17 @@ function styleToBounds(style, type, canvas) {
11240
11614
  }
11241
11615
  return { x, y, width, height };
11242
11616
  }
11243
- function buildLayer(id, groupId, bounds, zIndex) {
11244
- return {
11245
- id,
11246
- group: groupId,
11247
- state: { visible: true, locked: false },
11248
- bounds: {
11249
- x: bounds.x,
11250
- y: bounds.y,
11251
- width: bounds.width,
11252
- height: bounds.height,
11253
- scale: [1, 1],
11254
- zIndex
11255
- }
11256
- };
11257
- }
11258
- function buildModule(id, type, title, content, css = {}) {
11259
- return {
11260
- id,
11261
- version: 1,
11262
- loaded: true,
11263
- settings: { type, title, locked: false },
11264
- lights: [],
11265
- css,
11617
+ function buildChild(groupId, zIndex, bounds, type, title, content, css = {}) {
11618
+ const { layer, module } = buildModuleEnvelope({
11619
+ type,
11620
+ title,
11621
+ bounds,
11266
11622
  content,
11267
- variables: {},
11268
- events: {}
11269
- };
11623
+ css,
11624
+ parentGroupId: groupId,
11625
+ zIndex
11626
+ });
11627
+ return { layer, module };
11270
11628
  }
11271
11629
  function textStyleToCss(style) {
11272
11630
  const s = style ?? {};
@@ -11281,80 +11639,69 @@ function textStyleToCss(style) {
11281
11639
  return out;
11282
11640
  }
11283
11641
  function mapCompositeField(name, field, groupId, zIndex, provider, canvas) {
11284
- const id = nanoid8();
11285
11642
  switch (field.type) {
11286
11643
  case "video": {
11287
- const bounds = styleToBounds(field.style, "video", canvas);
11288
- const layer = buildLayer(id, groupId, bounds, zIndex);
11289
11644
  const src = pickFieldMediaHref(field.media, "video/");
11290
11645
  const volume = typeof field.volume === "number" ? field.volume : 1;
11291
- const module = buildModule(id, "video", name, {
11292
- src,
11293
- volume,
11294
- loop: true,
11295
- autoplay: true,
11296
- muted: volume === 0
11297
- });
11298
- return { layer, module };
11646
+ return buildChild(
11647
+ groupId,
11648
+ zIndex,
11649
+ styleToBounds(field.style, "video", canvas),
11650
+ "video",
11651
+ name,
11652
+ { src, volume, loop: true, autoplay: true, muted: volume === 0 }
11653
+ );
11299
11654
  }
11300
11655
  case "image": {
11301
- const bounds = styleToBounds(field.style, "image", canvas);
11302
- const layer = buildLayer(id, groupId, bounds, zIndex);
11303
11656
  const src = pickFieldMediaHref(field.media, "image/");
11304
- const module = buildModule(id, "image", name, { src });
11305
- return { layer, module };
11657
+ return buildChild(
11658
+ groupId,
11659
+ zIndex,
11660
+ styleToBounds(field.style, "image", canvas),
11661
+ "image",
11662
+ name,
11663
+ { src }
11664
+ );
11306
11665
  }
11307
11666
  case "text": {
11308
- const bounds = styleToBounds(field.style, "text", canvas);
11309
- const layer = buildLayer(id, groupId, bounds, zIndex);
11310
11667
  const raw = field.text?.[0]?.content ?? "";
11311
11668
  const value = substituteElementTokens(raw, { provider });
11312
- const module = buildModule(
11313
- id,
11669
+ return buildChild(
11670
+ groupId,
11671
+ zIndex,
11672
+ styleToBounds(field.style, "text", canvas),
11314
11673
  "text",
11315
11674
  name,
11316
11675
  { value, highlightColor: "inherit" },
11317
11676
  textStyleToCss(field.style)
11318
11677
  );
11319
- return { layer, module };
11320
11678
  }
11321
11679
  case "audio": {
11322
- const layer = buildLayer(id, groupId, { x: 0, y: 0, width: 1, height: 1 }, zIndex);
11323
11680
  const src = pickFieldMediaHref(field.media, "audio/");
11324
11681
  const volume = typeof field.volume === "number" ? field.volume : 0.5;
11325
- const module = buildModule(id, "audio", name, { src, volume, loop: false });
11326
- return { layer, module };
11682
+ return buildChild(
11683
+ groupId,
11684
+ zIndex,
11685
+ { x: 0, y: 0, width: 1, height: 1 },
11686
+ "audio",
11687
+ name,
11688
+ { src, volume, loop: false }
11689
+ );
11327
11690
  }
11328
11691
  default:
11329
11692
  return null;
11330
11693
  }
11331
11694
  }
11332
11695
  function buildElementScene(state, options) {
11333
- const groupId = nanoid8();
11334
11696
  const groupTitle = typeof state.displayName === "string" && state.displayName || state.referenceId || "Scene";
11335
- const groupLayer = {
11336
- id: groupId,
11697
+ const { id: groupId, layer: groupLayer, module: groupModule } = buildModuleEnvelope({
11337
11698
  type: "group",
11338
- group: null,
11339
- bounds: {
11340
- x: 0,
11341
- y: 0,
11342
- width: options.canvas.width,
11343
- height: options.canvas.height,
11344
- scale: [1, 1]
11345
- },
11346
- state: { visible: true, locked: false, expanded: false }
11347
- };
11348
- const groupModule = {
11349
- id: groupId,
11350
- loaded: true,
11351
- settings: { type: "group", title: groupTitle },
11352
- lights: [],
11353
- css: {},
11354
- content: {},
11355
- variables: {},
11356
- events: {}
11357
- };
11699
+ title: groupTitle,
11700
+ bounds: { x: 0, y: 0, width: options.canvas.width, height: options.canvas.height },
11701
+ type_layer: "group",
11702
+ parentGroupId: null,
11703
+ expanded: false
11704
+ });
11358
11705
  const children = [];
11359
11706
  const fields = state.settings?.compositeFields ?? {};
11360
11707
  let zIndex = 1;
@@ -11373,7 +11720,12 @@ function buildElementScene(state, options) {
11373
11720
  }
11374
11721
 
11375
11722
  // src/se-import/element-dispatch.ts
11376
- var IMPORT_META_KEY2 = "importMeta";
11723
+ function buildElementDescription(widgetInstanceId, sourceUrl) {
11724
+ return buildImportDescription(
11725
+ `Imported from StreamElements Element widget ${widgetInstanceId}`,
11726
+ sourceUrl
11727
+ );
11728
+ }
11377
11729
  function elementAlertKindToLumiaKeys(kind, provider) {
11378
11730
  switch (kind) {
11379
11731
  case "follower":
@@ -11392,9 +11744,9 @@ function elementAlertKindToLumiaKeys(kind, provider) {
11392
11744
  return alertBoxKeysFor("charityCampaignDonation", provider);
11393
11745
  case "subscriberGift":
11394
11746
  case "communityGiftPurchase": {
11395
- if (provider === "twitch") return ["twitch-giftSubscription"];
11396
- if (provider === "kick") return ["kick-subscriptionGift"];
11397
- if (provider === "youtube") return ["youtube-giftMembers"];
11747
+ if (provider === "twitch") return [LumiaAlertValues3.TWITCH_GIFT_SUBSCRIPTION];
11748
+ if (provider === "kick") return [LumiaAlertValues3.KICK_GIFT_SUBSCRIPTION];
11749
+ if (provider === "youtube") return [LumiaAlertValues3.YOUTUBE_GIFT_MEMBERS];
11398
11750
  return alertBoxKeysFor("subscriber", provider);
11399
11751
  }
11400
11752
  default:
@@ -11409,7 +11761,7 @@ function buildCombinedAlertModule(alertStates, provider, canvas) {
11409
11761
  for (const state of alertStates) {
11410
11762
  const cls = classifyConfigState(state);
11411
11763
  if (cls.kind !== "alert") continue;
11412
- const { event, bounds: bounds2 } = buildElementAlertEvent(state, { provider });
11764
+ const { event, bounds: bounds2 } = buildElementAlertEvent(state, { provider, canvas });
11413
11765
  const keys = elementAlertKindToLumiaKeys(cls.alertKind, provider);
11414
11766
  if (keys.length === 0) continue;
11415
11767
  for (const key of keys) {
@@ -11421,61 +11773,42 @@ function buildCombinedAlertModule(alertStates, provider, canvas) {
11421
11773
  }
11422
11774
  }
11423
11775
  if (Object.keys(events).length === 0) return null;
11424
- const id = nanoid9();
11425
11776
  const bounds = firstBounds ?? {
11426
11777
  x: Math.round(canvas.width * 0.6),
11427
11778
  y: Math.round(canvas.height * 0.15),
11428
11779
  width: 400,
11429
11780
  height: 300
11430
11781
  };
11431
- const layer = {
11432
- id,
11433
- state: { visible: true, locked: false },
11434
- bounds: {
11435
- x: bounds.x,
11436
- y: bounds.y,
11437
- width: bounds.width,
11438
- height: bounds.height,
11439
- scale: [1, 1]
11440
- }
11441
- };
11442
11782
  const baseAlert = firstEvent ? stripEventOnlyFields2(firstEvent) : {};
11443
- const module = {
11444
- id,
11445
- version: 1,
11446
- loaded: true,
11447
- settings: { type: "alert", title: "Alerts (Element import)", locked: false },
11448
- lights: [],
11449
- css: {},
11450
- content: {},
11451
- variables: {},
11452
- events: {},
11783
+ const { layer, module } = buildModuleEnvelope({
11784
+ type: "alert",
11785
+ title: "Alerts (Element import)",
11786
+ bounds,
11453
11787
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
11454
11788
  alert: { ...baseAlert, events }
11455
- };
11789
+ });
11456
11790
  return { layer, module };
11457
11791
  }
11458
11792
  function stripEventOnlyFields2(event) {
11459
- const { on: _on, variations: _variations, matchEmptyCondition: _mec, ...base } = event;
11793
+ const {
11794
+ on: _on,
11795
+ variations: _variations,
11796
+ matchEmptyCondition: _mec,
11797
+ ...base
11798
+ } = event;
11460
11799
  return base;
11461
11800
  }
11462
11801
  function stampImportMeta2(module, state, role) {
11463
- module.content = {
11464
- ...module.content ?? {},
11465
- [IMPORT_META_KEY2]: {
11466
- source: "streamelements-element",
11467
- role,
11468
- referenceId: state?.referenceId,
11469
- categoryId: state?.categoryId,
11470
- displayName: state?.displayName,
11471
- importedAt: (/* @__PURE__ */ new Date()).toISOString()
11472
- }
11473
- };
11802
+ stampImportMetaMerge(module, {
11803
+ source: "streamelements-element",
11804
+ role,
11805
+ referenceId: state?.referenceId,
11806
+ categoryId: state?.categoryId,
11807
+ displayName: state?.displayName
11808
+ });
11474
11809
  }
11475
- function importElementOverlay(response) {
11476
- const configData = parseElementConfigData(
11477
- response.widgetInstanceConfigVersion.configData
11478
- );
11810
+ function importElementOverlay(response, options = {}) {
11811
+ const configData = parseElementConfigData(response.widgetInstanceConfigVersion.configData);
11479
11812
  if (!configData) {
11480
11813
  throw new Error(
11481
11814
  "StreamElements Element response carries an invalid configData payload. The widget may be corrupted or the response format may have changed."
@@ -11545,7 +11878,10 @@ function importElementOverlay(response) {
11545
11878
  uuid: "",
11546
11879
  listen_id: "",
11547
11880
  name: response.widgetInstance.displayName ? `[SE Element] ${response.widgetInstance.displayName}` : `Imported from StreamElements Element (${response.widgetInstance.widgetInstanceId})`,
11548
- description: `Imported from StreamElements Element widget ${response.widgetInstance.widgetInstanceId}`,
11881
+ description: buildElementDescription(
11882
+ response.widgetInstance.widgetInstanceId,
11883
+ options.sourceUrl
11884
+ ),
11549
11885
  settings: {
11550
11886
  metadata: { width: canvas.width, height: canvas.height },
11551
11887
  layers,
@@ -11559,7 +11895,7 @@ function importElementOverlay(response) {
11559
11895
  }
11560
11896
 
11561
11897
  // src/se-import/index.ts
11562
- var IMPORT_META_KEY3 = "importMeta";
11898
+ var IMPORT_META_KEY2 = "importMeta";
11563
11899
  var REVIEW_REASONS = {
11564
11900
  "se-widget-bit-boss": "No native Lumia equivalent \u2014 bit boss fight bar with HP/damage states.",
11565
11901
  // `se-widget-hype-cup` intentionally omitted — it now routes to the native
@@ -11589,6 +11925,10 @@ function reasonFor(seType, status, flaggedOff) {
11589
11925
  if (flaggedOff && FLAG_OFF_REASONS[seType]) return FLAG_OFF_REASONS[seType];
11590
11926
  return REVIEW_REASONS[seType] ?? (status === "template" ? "Imported as a generic template stub." : "No native mapping yet \u2014 imported as a labelled text placeholder.");
11591
11927
  }
11928
+ function buildSEBootstrapDescription(overlayId, sourceUrl) {
11929
+ const head = overlayId ? `Imported from StreamElements overlay ${overlayId}` : "Imported from StreamElements";
11930
+ return buildImportDescription(head, sourceUrl);
11931
+ }
11592
11932
  function extractSEOverlayId(input) {
11593
11933
  const trimmed = input.trim();
11594
11934
  const m = trimmed.match(/[0-9a-f]{24}/i);
@@ -11643,7 +11983,7 @@ function isSEBootstrap(value) {
11643
11983
  const o = v.overlay;
11644
11984
  return Array.isArray(o.widgets);
11645
11985
  }
11646
- function importSEBootstrap(bootstrap) {
11986
+ function importSEBootstrap(bootstrap, options = {}) {
11647
11987
  const seOverlay = bootstrap.overlay;
11648
11988
  const widgets = seOverlay?.widgets ?? [];
11649
11989
  const coverage = {
@@ -11666,7 +12006,7 @@ function importSEBootstrap(bootstrap) {
11666
12006
  if (w.type !== "se-widget-group") continue;
11667
12007
  const uid = w.variables?.uid;
11668
12008
  if (typeof uid !== "string") continue;
11669
- const lumiaGroupId = nanoid10();
12009
+ const lumiaGroupId = nanoid8();
11670
12010
  seUidToLumiaGroupId[uid] = lumiaGroupId;
11671
12011
  groupWidgetByUid[uid] = w;
11672
12012
  }
@@ -11705,7 +12045,7 @@ function importSEBootstrap(bootstrap) {
11705
12045
  lights: [],
11706
12046
  css: {},
11707
12047
  content: {
11708
- [IMPORT_META_KEY3]: {
12048
+ [IMPORT_META_KEY2]: {
11709
12049
  source: "streamelements",
11710
12050
  widgetType: w.type,
11711
12051
  importedAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -11743,7 +12083,7 @@ function importSEBootstrap(bootstrap) {
11743
12083
  if (result.status === "placeholder" || result.status === "template") {
11744
12084
  module.content = {
11745
12085
  ...module.content ?? {},
11746
- [IMPORT_META_KEY3]: {
12086
+ [IMPORT_META_KEY2]: {
11747
12087
  source: "streamelements",
11748
12088
  widgetType: w.type,
11749
12089
  importedAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -11783,7 +12123,7 @@ function importSEBootstrap(bootstrap) {
11783
12123
  uuid: "",
11784
12124
  listen_id: "",
11785
12125
  name: seOverlay?.name ? `[SE] ${seOverlay.name}` : `Imported from StreamElements (${seOverlay?._id ?? ""})`,
11786
- description: seOverlay?._id ? `Imported from StreamElements overlay ${seOverlay._id}` : "Imported from StreamElements",
12126
+ description: buildSEBootstrapDescription(seOverlay?._id, options.sourceUrl),
11787
12127
  settings: {
11788
12128
  metadata: {
11789
12129
  width: seOverlay?.settings?.width ?? 1920,
@@ -11831,7 +12171,7 @@ function applyReviewAction(result, moduleId, action, payload) {
11831
12171
  bounds: existingLayer.bounds,
11832
12172
  state: existingLayer.state
11833
12173
  };
11834
- const importMeta2 = existing2.content?.[IMPORT_META_KEY3];
12174
+ const importMeta2 = existing2.content?.[IMPORT_META_KEY2];
11835
12175
  const mergedLights = (existing2.lights?.length ?? 0) > 0 ? existing2.lights : transplant.module.lights;
11836
12176
  const mergedEvents = existing2.events && Object.keys(existing2.events).length > 0 ? { ...transplant.module.events, ...existing2.events } : transplant.module.events;
11837
12177
  const mergedVariables = existing2.variables && Object.keys(existing2.variables).length > 0 ? { ...transplant.module.variables, ...existing2.variables } : transplant.module.variables;
@@ -11843,7 +12183,7 @@ function applyReviewAction(result, moduleId, action, payload) {
11843
12183
  variables: mergedVariables,
11844
12184
  content: {
11845
12185
  ...transplant.module.content ?? {},
11846
- [IMPORT_META_KEY3]: importMeta2
12186
+ [IMPORT_META_KEY2]: importMeta2
11847
12187
  }
11848
12188
  };
11849
12189
  const rootOldNewId = transplant.layer.id;
@@ -11871,7 +12211,7 @@ function applyReviewAction(result, moduleId, action, payload) {
11871
12211
  const existing = modules[moduleId];
11872
12212
  const generated = payload;
11873
12213
  if (!existing || !generated) return result;
11874
- const importMeta = existing.content?.[IMPORT_META_KEY3];
12214
+ const importMeta = existing.content?.[IMPORT_META_KEY2];
11875
12215
  const replaced = {
11876
12216
  ...existing,
11877
12217
  settings: { ...existing.settings ?? {}, type: "custom" },
@@ -11885,7 +12225,7 @@ function applyReviewAction(result, moduleId, action, payload) {
11885
12225
  data: parseLooseJson(generated.data) ?? {},
11886
12226
  configs: parseLooseJson(generated.configs) ?? [],
11887
12227
  flavor: "ai-generated",
11888
- [IMPORT_META_KEY3]: importMeta
12228
+ [IMPORT_META_KEY2]: importMeta
11889
12229
  }
11890
12230
  };
11891
12231
  modules[moduleId] = replaced;