diff --git a/dist/better-xcloud.meta.js b/dist/better-xcloud.meta.js
index 02a4f7dd..ce2e438c 100644
--- a/dist/better-xcloud.meta.js
+++ b/dist/better-xcloud.meta.js
@@ -1,5 +1,5 @@
// ==UserScript==
// @name Better xCloud
// @namespace https://github.com/redphx
-// @version 5.5.0
+// @version 5.6.0
// ==/UserScript==
diff --git a/dist/better-xcloud.user.js b/dist/better-xcloud.user.js
index 8e0c09c1..aba6a298 100644
--- a/dist/better-xcloud.user.js
+++ b/dist/better-xcloud.user.js
@@ -1,7 +1,7 @@
// ==UserScript==
// @name Better xCloud
// @namespace https://github.com/redphx
-// @version 5.5.1-beta
+// @version 5.6.0
// @description Improve Xbox Cloud Gaming (xCloud) experience
// @author redphx
// @license MIT
@@ -13,6 +13,17 @@
// @downloadURL https://github.com/redphx/better-xcloud/releases/latest/download/better-xcloud.user.js
// ==/UserScript==
"use strict";
+var UserAgentProfile;
+(function(UserAgentProfile2) {
+ UserAgentProfile2["WINDOWS_EDGE"] = "windows-edge";
+ UserAgentProfile2["MACOS_SAFARI"] = "macos-safari";
+ UserAgentProfile2["SMART_TV_GENERIC"] = "smarttv-generic";
+ UserAgentProfile2["SMART_TV_TIZEN"] = "smarttv-tizen";
+ UserAgentProfile2["VR_OCULUS"] = "vr-oculus";
+ UserAgentProfile2["DEFAULT"] = "default";
+ UserAgentProfile2["CUSTOM"] = "custom";
+})(UserAgentProfile || (UserAgentProfile = {}));
+
/* ADDITIONAL CODE */
@@ -48,22 +59,22 @@ class UserAgent {
static #isSafari = null;
static #isSafariMobile = null;
static #USER_AGENTS = {
- "windows-edge": `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${CHROMIUM_VERSION} Safari/537.36 Edg/${CHROMIUM_VERSION}`,
- "macos-safari": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5.2 Safari/605.1.1",
- "smarttv-generic": `${window.navigator.userAgent} SmartTV`,
- "smarttv-tizen": `Mozilla/5.0 (SMART-TV; LINUX; Tizen 7.0) AppleWebKit/537.36 (KHTML, like Gecko) ${CHROMIUM_VERSION}/7.0 TV Safari/537.36 ${SMART_TV_UNIQUE_ID}`,
- "vr-oculus": window.navigator.userAgent + " OculusBrowser VR"
+ [UserAgentProfile.WINDOWS_EDGE]: `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${CHROMIUM_VERSION} Safari/537.36 Edg/${CHROMIUM_VERSION}`,
+ [UserAgentProfile.MACOS_SAFARI]: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5.2 Safari/605.1.1",
+ [UserAgentProfile.SMART_TV_GENERIC]: `${window.navigator.userAgent} SmartTV`,
+ [UserAgentProfile.SMART_TV_TIZEN]: `Mozilla/5.0 (SMART-TV; LINUX; Tizen 7.0) AppleWebKit/537.36 (KHTML, like Gecko) ${CHROMIUM_VERSION}/7.0 TV Safari/537.36 ${SMART_TV_UNIQUE_ID}`,
+ [UserAgentProfile.VR_OCULUS]: window.navigator.userAgent + " OculusBrowser VR"
};
static init() {
if (UserAgent.#config = JSON.parse(window.localStorage.getItem(UserAgent.STORAGE_KEY) || "{}"), !UserAgent.#config.profile)
- UserAgent.#config.profile = "default";
+ UserAgent.#config.profile = UserAgentProfile.DEFAULT;
if (!UserAgent.#config.custom)
UserAgent.#config.custom = "";
UserAgent.spoof();
}
static updateStorage(profile, custom) {
const config = UserAgent.#config;
- if (config.profile = profile, profile === "custom" && typeof custom !== "undefined")
+ if (config.profile = profile, profile === UserAgentProfile.CUSTOM && typeof custom !== "undefined")
config.custom = custom;
window.localStorage.setItem(UserAgent.STORAGE_KEY, JSON.stringify(config));
}
@@ -73,9 +84,9 @@ class UserAgent {
static get(profile) {
const defaultUserAgent = window.navigator.userAgent;
switch (profile) {
- case "default":
+ case UserAgentProfile.DEFAULT:
return defaultUserAgent;
- case "custom":
+ case UserAgentProfile.CUSTOM:
return UserAgent.#config.custom || defaultUserAgent;
default:
return UserAgent.#USER_AGENTS[profile] || defaultUserAgent;
@@ -102,7 +113,7 @@ class UserAgent {
}
static spoof() {
const profile = UserAgent.#config.profile;
- if (profile === "default")
+ if (profile === UserAgentProfile.DEFAULT)
return;
let newUserAgent = UserAgent.get(profile);
if (BX_FLAGS.IsSupportedTvBrowser)
@@ -122,7 +133,7 @@ function deepClone(obj) {
return {};
return JSON.parse(JSON.stringify(obj));
}
-var SCRIPT_VERSION = "5.5.1-beta", AppInterface = window.AppInterface;
+var SCRIPT_VERSION = "5.6.0", AppInterface = window.AppInterface;
UserAgent.init();
var userAgent = window.navigator.userAgent.toLowerCase(), isTv = userAgent.includes("smart-tv") || userAgent.includes("smarttv") || /\baft.*\b/.test(userAgent), isVr = window.navigator.userAgent.includes("VR") && window.navigator.userAgent.includes("OculusBrowser"), browserHasTouchSupport = "ontouchstart" in window || navigator.maxTouchPoints > 0, userAgentHasTouchSupport = !isTv && !isVr && browserHasTouchSupport, STATES = {
supportedRegion: !0,
@@ -150,7 +161,7 @@ var userAgent = window.navigator.userAgent.toLowerCase(), isTv = userAgent.inclu
}, STORAGE = {};
var BxEvent;
-((BxEvent) => {
+(function(BxEvent) {
BxEvent.JUMP_BACK_IN_READY = "bx-jump-back-in-ready", BxEvent.POPSTATE = "bx-popstate", BxEvent.TITLE_INFO_READY = "bx-title-info-ready", BxEvent.SETTINGS_CHANGED = "bx-settings-changed", BxEvent.STREAM_LOADING = "bx-stream-loading", BxEvent.STREAM_STARTING = "bx-stream-starting", BxEvent.STREAM_STARTED = "bx-stream-started", BxEvent.STREAM_PLAYING = "bx-stream-playing", BxEvent.STREAM_STOPPED = "bx-stream-stopped", BxEvent.STREAM_ERROR_PAGE = "bx-stream-error-page", BxEvent.STREAM_WEBRTC_CONNECTED = "bx-stream-webrtc-connected", BxEvent.STREAM_WEBRTC_DISCONNECTED = "bx-stream-webrtc-disconnected", BxEvent.STREAM_SESSION_READY = "bx-stream-session-ready", BxEvent.CUSTOM_TOUCH_LAYOUTS_LOADED = "bx-custom-touch-layouts-loaded", BxEvent.TOUCH_LAYOUT_MANAGER_READY = "bx-touch-layout-manager-ready", BxEvent.REMOTE_PLAY_READY = "bx-remote-play-ready", BxEvent.REMOTE_PLAY_FAILED = "bx-remote-play-failed", BxEvent.XCLOUD_SERVERS_READY = "bx-servers-ready", BxEvent.XCLOUD_SERVERS_UNAVAILABLE = "bx-servers-unavailable", BxEvent.DATA_CHANNEL_CREATED = "bx-data-channel-created", BxEvent.GAME_BAR_ACTION_ACTIVATED = "bx-game-bar-action-activated", BxEvent.MICROPHONE_STATE_CHANGED = "bx-microphone-state-changed", BxEvent.CAPTURE_SCREENSHOT = "bx-capture-screenshot", BxEvent.POINTER_LOCK_REQUESTED = "bx-pointer-lock-requested", BxEvent.POINTER_LOCK_EXITED = "bx-pointer-lock-exited", BxEvent.NAVIGATION_FOCUS_CHANGED = "bx-nav-focus-changed", BxEvent.XCLOUD_DIALOG_SHOWN = "bx-xcloud-dialog-shown", BxEvent.XCLOUD_DIALOG_DISMISSED = "bx-xcloud-dialog-dismissed", BxEvent.XCLOUD_GUIDE_MENU_SHOWN = "bx-xcloud-guide-menu-shown", BxEvent.XCLOUD_POLLING_MODE_CHANGED = "bx-xcloud-polling-mode-changed", BxEvent.XCLOUD_RENDERING_COMPONENT = "bx-xcloud-rendering-page";
function dispatch(target, eventName, data) {
if (!target)
@@ -166,9 +177,20 @@ var BxEvent;
target.dispatchEvent(event), AppInterface && AppInterface.onEvent(eventName);
}
BxEvent.dispatch = dispatch;
-})(BxEvent ||= {});
+})(BxEvent || (BxEvent = {}));
window.BxEvent = BxEvent;
+var StreamPlayerType;
+(function(StreamPlayerType2) {
+ StreamPlayerType2["VIDEO"] = "default";
+ StreamPlayerType2["WEBGL2"] = "webgl2";
+})(StreamPlayerType || (StreamPlayerType = {}));
+var StreamVideoProcessing;
+(function(StreamVideoProcessing2) {
+ StreamVideoProcessing2["USM"] = "usm";
+ StreamVideoProcessing2["CAS"] = "cas";
+})(StreamVideoProcessing || (StreamVideoProcessing = {}));
+
class NavigationUtils {
static setNearby($elm, nearby) {
$elm.nearby = $elm.nearby || {};
@@ -179,7 +201,7 @@ class NavigationUtils {
}
var setNearby = NavigationUtils.setNearby;
-function createElement(elmName, props = {}, ..._) {
+var createElement = function(elmName, props = {}, ..._) {
let $elm;
const hasNs = "xmlns" in props;
if (hasNs)
@@ -207,7 +229,7 @@ function createElement(elmName, props = {}, ..._) {
$elm.appendChild(document.createTextNode(arg));
}
return $elm;
-}
+};
function getReactProps($elm) {
for (let key in $elm)
if (key.startsWith("__reactProps"))
@@ -249,16 +271,102 @@ var ButtonStyleIndices = Object.keys(ButtonStyle).splice(0, Object.keys(ButtonSt
}, CTN = document.createTextNode.bind(document);
window.BX_CE = createElement;
+var StorageKey;
+(function(StorageKey2) {
+ StorageKey2["GLOBAL"] = "better_xcloud";
+})(StorageKey || (StorageKey = {}));
+var PrefKey;
+(function(PrefKey2) {
+ PrefKey2["LAST_UPDATE_CHECK"] = "version_last_check";
+ PrefKey2["LATEST_VERSION"] = "version_latest";
+ PrefKey2["CURRENT_VERSION"] = "version_current";
+ PrefKey2["BETTER_XCLOUD_LOCALE"] = "bx_locale";
+ PrefKey2["SERVER_REGION"] = "server_region";
+ PrefKey2["SERVER_BYPASS_RESTRICTION"] = "server_bypass_restriction";
+ PrefKey2["PREFER_IPV6_SERVER"] = "prefer_ipv6_server";
+ PrefKey2["STREAM_TARGET_RESOLUTION"] = "stream_target_resolution";
+ PrefKey2["STREAM_PREFERRED_LOCALE"] = "stream_preferred_locale";
+ PrefKey2["STREAM_CODEC_PROFILE"] = "stream_codec_profile";
+ PrefKey2["USER_AGENT_PROFILE"] = "user_agent_profile";
+ PrefKey2["STREAM_SIMPLIFY_MENU"] = "stream_simplify_menu";
+ PrefKey2["STREAM_COMBINE_SOURCES"] = "stream_combine_sources";
+ PrefKey2["STREAM_TOUCH_CONTROLLER"] = "stream_touch_controller";
+ PrefKey2["STREAM_TOUCH_CONTROLLER_AUTO_OFF"] = "stream_touch_controller_auto_off";
+ PrefKey2["STREAM_TOUCH_CONTROLLER_DEFAULT_OPACITY"] = "stream_touch_controller_default_opacity";
+ PrefKey2["STREAM_TOUCH_CONTROLLER_STYLE_STANDARD"] = "stream_touch_controller_style_standard";
+ PrefKey2["STREAM_TOUCH_CONTROLLER_STYLE_CUSTOM"] = "stream_touch_controller_style_custom";
+ PrefKey2["STREAM_DISABLE_FEEDBACK_DIALOG"] = "stream_disable_feedback_dialog";
+ PrefKey2["BITRATE_VIDEO_MAX"] = "bitrate_video_max";
+ PrefKey2["GAME_BAR_POSITION"] = "game_bar_position";
+ PrefKey2["LOCAL_CO_OP_ENABLED"] = "local_co_op_enabled";
+ PrefKey2["CONTROLLER_ENABLE_SHORTCUTS"] = "controller_enable_shortcuts";
+ PrefKey2["CONTROLLER_ENABLE_VIBRATION"] = "controller_enable_vibration";
+ PrefKey2["CONTROLLER_DEVICE_VIBRATION"] = "controller_device_vibration";
+ PrefKey2["CONTROLLER_VIBRATION_INTENSITY"] = "controller_vibration_intensity";
+ PrefKey2["CONTROLLER_SHOW_CONNECTION_STATUS"] = "controller_show_connection_status";
+ PrefKey2["NATIVE_MKB_ENABLED"] = "native_mkb_enabled";
+ PrefKey2["NATIVE_MKB_SCROLL_HORIZONTAL_SENSITIVITY"] = "native_mkb_scroll_x_sensitivity";
+ PrefKey2["NATIVE_MKB_SCROLL_VERTICAL_SENSITIVITY"] = "native_mkb_scroll_y_sensitivity";
+ PrefKey2["MKB_ENABLED"] = "mkb_enabled";
+ PrefKey2["MKB_HIDE_IDLE_CURSOR"] = "mkb_hide_idle_cursor";
+ PrefKey2["MKB_ABSOLUTE_MOUSE"] = "mkb_absolute_mouse";
+ PrefKey2["MKB_DEFAULT_PRESET_ID"] = "mkb_default_preset_id";
+ PrefKey2["SCREENSHOT_APPLY_FILTERS"] = "screenshot_apply_filters";
+ PrefKey2["BLOCK_TRACKING"] = "block_tracking";
+ PrefKey2["BLOCK_SOCIAL_FEATURES"] = "block_social_features";
+ PrefKey2["SKIP_SPLASH_VIDEO"] = "skip_splash_video";
+ PrefKey2["HIDE_DOTS_ICON"] = "hide_dots_icon";
+ PrefKey2["REDUCE_ANIMATIONS"] = "reduce_animations";
+ PrefKey2["UI_LOADING_SCREEN_GAME_ART"] = "ui_loading_screen_game_art";
+ PrefKey2["UI_LOADING_SCREEN_WAIT_TIME"] = "ui_loading_screen_wait_time";
+ PrefKey2["UI_LOADING_SCREEN_ROCKET"] = "ui_loading_screen_rocket";
+ PrefKey2["UI_CONTROLLER_FRIENDLY"] = "ui_controller_friendly";
+ PrefKey2["UI_LAYOUT"] = "ui_layout";
+ PrefKey2["UI_SCROLLBAR_HIDE"] = "ui_scrollbar_hide";
+ PrefKey2["UI_HIDE_SECTIONS"] = "ui_hide_sections";
+ PrefKey2["UI_HOME_CONTEXT_MENU_DISABLED"] = "ui_home_context_menu_disabled";
+ PrefKey2["UI_GAME_CARD_SHOW_WAIT_TIME"] = "ui_game_card_show_wait_time";
+ PrefKey2["VIDEO_PLAYER_TYPE"] = "video_player_type";
+ PrefKey2["VIDEO_PROCESSING"] = "video_processing";
+ PrefKey2["VIDEO_POWER_PREFERENCE"] = "video_power_preference";
+ PrefKey2["VIDEO_SHARPNESS"] = "video_sharpness";
+ PrefKey2["VIDEO_RATIO"] = "video_ratio";
+ PrefKey2["VIDEO_BRIGHTNESS"] = "video_brightness";
+ PrefKey2["VIDEO_CONTRAST"] = "video_contrast";
+ PrefKey2["VIDEO_SATURATION"] = "video_saturation";
+ PrefKey2["AUDIO_MIC_ON_PLAYING"] = "audio_mic_on_playing";
+ PrefKey2["AUDIO_ENABLE_VOLUME_CONTROL"] = "audio_enable_volume_control";
+ PrefKey2["AUDIO_VOLUME"] = "audio_volume";
+ PrefKey2["STATS_ITEMS"] = "stats_items";
+ PrefKey2["STATS_SHOW_WHEN_PLAYING"] = "stats_show_when_playing";
+ PrefKey2["STATS_QUICK_GLANCE"] = "stats_quick_glance";
+ PrefKey2["STATS_POSITION"] = "stats_position";
+ PrefKey2["STATS_TEXT_SIZE"] = "stats_text_size";
+ PrefKey2["STATS_TRANSPARENT"] = "stats_transparent";
+ PrefKey2["STATS_OPACITY"] = "stats_opacity";
+ PrefKey2["STATS_CONDITIONAL_FORMATTING"] = "stats_conditional_formatting";
+ PrefKey2["REMOTE_PLAY_ENABLED"] = "xhome_enabled";
+ PrefKey2["REMOTE_PLAY_RESOLUTION"] = "xhome_resolution";
+ PrefKey2["GAME_FORTNITE_FORCE_CONSOLE"] = "game_fortnite_force_console";
+})(PrefKey || (PrefKey = {}));
+
+var TextColor;
+(function(TextColor2) {
+ TextColor2["INFO"] = "#008746";
+ TextColor2["WARNING"] = "#c1a404";
+ TextColor2["ERROR"] = "#c10404";
+})(TextColor || (TextColor = {}));
+
class BxLogger {
static #PREFIX = "[BxC]";
static info(tag, ...args) {
- BxLogger.#log("#008746", tag, ...args);
+ BxLogger.#log(TextColor.INFO, tag, ...args);
}
static warning(tag, ...args) {
- BxLogger.#log("#c1a404", tag, ...args);
+ BxLogger.#log(TextColor.WARNING, tag, ...args);
}
static error(tag, ...args) {
- BxLogger.#log("#c10404", tag, ...args);
+ BxLogger.#log(TextColor.ERROR, tag, ...args);
}
static #log(color, tag, ...args) {
console.log(`%c${BxLogger.#PREFIX}`, `color:${color};font-weight:bold;`, tag, "//", ...args);
@@ -514,6 +622,7 @@ var SUPPORTED_LANGUAGES = {
"support-better-xcloud": "Support Better xCloud",
"swap-buttons": "Swap buttons",
"take-screenshot": "Take screenshot",
+ "take-recording": "Take Recording",
"target-resolution": "Target resolution",
"tc-all-games": "All games",
"tc-all-white": "All white",
@@ -670,6 +779,26 @@ var BypassServers = {
us: "143.244.47.65"
};
+var UiSection;
+(function(UiSection2) {
+ UiSection2["ALL_GAMES"] = "all-games";
+ UiSection2["FRIENDS"] = "friends";
+ UiSection2["MOST_POPULAR"] = "most-popular";
+ UiSection2["NATIVE_MKB"] = "native-mkb";
+ UiSection2["NEWS"] = "news";
+ UiSection2["TOUCH"] = "touch";
+})(UiSection || (UiSection = {}));
+
+var StreamStat;
+(function(StreamStat2) {
+ StreamStat2["PING"] = "ping";
+ StreamStat2["FPS"] = "fps";
+ StreamStat2["BITRATE"] = "btr";
+ StreamStat2["DECODE_TIME"] = "dt";
+ StreamStat2["PACKETS_LOST"] = "pl";
+ StreamStat2["FRAMES_LOST"] = "fl";
+})(StreamStat || (StreamStat = {}));
+
class StreamStats {
static instance;
static getInstance() {
@@ -743,7 +872,7 @@ class StreamStats {
return;
}
this.#timeoutId = null;
- const startTime = performance.now(), PREF_STATS_CONDITIONAL_FORMATTING = getPref("stats_conditional_formatting"), stats = await STATES.currentStream.peerConnection.getStats();
+ const startTime = performance.now(), PREF_STATS_CONDITIONAL_FORMATTING = getPref(PrefKey.STATS_CONDITIONAL_FORMATTING), stats = await STATES.currentStream.peerConnection.getStats();
let grade = "";
stats.forEach((stat) => {
if (stat.type === "inbound-rtp" && stat.kind === "video") {
@@ -775,21 +904,21 @@ class StreamStats {
this.#timeoutId = window.setTimeout(this.#update.bind(this), this.#updateInterval - lapsedTime);
}
refreshStyles() {
- const PREF_ITEMS = getPref("stats_items"), PREF_POSITION = getPref("stats_position"), PREF_TRANSPARENT = getPref("stats_transparent"), PREF_OPACITY = getPref("stats_opacity"), PREF_TEXT_SIZE = getPref("stats_text_size"), $container = this.#$container;
+ const PREF_ITEMS = getPref(PrefKey.STATS_ITEMS), PREF_POSITION = getPref(PrefKey.STATS_POSITION), PREF_TRANSPARENT = getPref(PrefKey.STATS_TRANSPARENT), PREF_OPACITY = getPref(PrefKey.STATS_OPACITY), PREF_TEXT_SIZE = getPref(PrefKey.STATS_TEXT_SIZE), $container = this.#$container;
$container.dataset.stats = "[" + PREF_ITEMS.join("][") + "]", $container.dataset.position = PREF_POSITION, $container.dataset.transparent = PREF_TRANSPARENT, $container.style.opacity = PREF_OPACITY + "%", $container.style.fontSize = PREF_TEXT_SIZE;
}
hideSettingsUi() {
- if (this.isGlancing() && !getPref("stats_quick_glance"))
+ if (this.isGlancing() && !getPref(PrefKey.STATS_QUICK_GLANCE))
this.stop();
}
#render() {
const stats = {
- ["ping"]: [t("stat-ping"), this.#$ping = CE("span", {}, "0")],
- ["fps"]: [t("stat-fps"), this.#$fps = CE("span", {}, "0")],
- ["btr"]: [t("stat-bitrate"), this.#$br = CE("span", {}, "0 Mbps")],
- ["dt"]: [t("stat-decode-time"), this.#$dt = CE("span", {}, "0ms")],
- ["pl"]: [t("stat-packets-lost"), this.#$pl = CE("span", {}, "0")],
- ["fl"]: [t("stat-frames-lost"), this.#$fl = CE("span", {}, "0")]
+ [StreamStat.PING]: [t("stat-ping"), this.#$ping = CE("span", {}, "0")],
+ [StreamStat.FPS]: [t("stat-fps"), this.#$fps = CE("span", {}, "0")],
+ [StreamStat.BITRATE]: [t("stat-bitrate"), this.#$br = CE("span", {}, "0 Mbps")],
+ [StreamStat.DECODE_TIME]: [t("stat-decode-time"), this.#$dt = CE("span", {}, "0ms")],
+ [StreamStat.PACKETS_LOST]: [t("stat-packets-lost"), this.#$pl = CE("span", {}, "0")],
+ [StreamStat.FRAMES_LOST]: [t("stat-frames-lost"), this.#$fl = CE("span", {}, "0")]
}, $barFragment = document.createDocumentFragment();
let statKey;
for (statKey in stats) {
@@ -803,7 +932,7 @@ class StreamStats {
}
static setupEvents() {
window.addEventListener(BxEvent.STREAM_PLAYING, (e) => {
- const PREF_STATS_QUICK_GLANCE = getPref("stats_quick_glance"), PREF_STATS_SHOW_WHEN_PLAYING = getPref("stats_show_when_playing"), streamStats = StreamStats.getInstance();
+ const PREF_STATS_QUICK_GLANCE = getPref(PrefKey.STATS_QUICK_GLANCE), PREF_STATS_SHOW_WHEN_PLAYING = getPref(PrefKey.STATS_SHOW_WHEN_PLAYING), streamStats = StreamStats.getInstance();
if (PREF_STATS_SHOW_WHEN_PLAYING)
streamStats.start();
else if (PREF_STATS_QUICK_GLANCE)
@@ -815,6 +944,15 @@ class StreamStats {
}
}
+var SettingElementType;
+(function(SettingElementType2) {
+ SettingElementType2["OPTIONS"] = "options";
+ SettingElementType2["MULTIPLE_OPTIONS"] = "multiple-options";
+ SettingElementType2["NUMBER"] = "number";
+ SettingElementType2["NUMBER_STEPPER"] = "number-stepper";
+ SettingElementType2["CHECKBOX"] = "checkbox";
+})(SettingElementType || (SettingElementType = {}));
+
class SettingElement {
static #renderOptions(key, setting, currentValue, onChange) {
const $control = CE("select", {
@@ -960,17 +1098,17 @@ class SettingElement {
}), $wrapper;
}
static #METHOD_MAP = {
- ["options"]: SettingElement.#renderOptions,
- ["multiple-options"]: SettingElement.#renderMultipleOptions,
- ["number"]: SettingElement.#renderNumber,
- ["number-stepper"]: SettingElement.#renderNumberStepper,
- ["checkbox"]: SettingElement.#renderCheckbox
+ [SettingElementType.OPTIONS]: SettingElement.#renderOptions,
+ [SettingElementType.MULTIPLE_OPTIONS]: SettingElement.#renderMultipleOptions,
+ [SettingElementType.NUMBER]: SettingElement.#renderNumber,
+ [SettingElementType.NUMBER_STEPPER]: SettingElement.#renderNumberStepper,
+ [SettingElementType.CHECKBOX]: SettingElement.#renderCheckbox
};
static render(type, key, setting, currentValue, onChange, options) {
const method = SettingElement.#METHOD_MAP[type], $control = method(...Array.from(arguments).slice(1));
- if (type !== "number-stepper")
+ if (type !== SettingElementType.NUMBER_STEPPER)
$control.id = `bx_setting_${key}`;
- if (type === "options" || type === "multiple-options")
+ if (type === SettingElementType.OPTIONS || type === SettingElementType.MULTIPLE_OPTIONS)
$control.name = $control.id;
return $control;
}
@@ -980,13 +1118,13 @@ class SettingElement {
if ("type" in definition)
type = definition.type;
else if ("options" in definition)
- type = "options";
+ type = SettingElementType.OPTIONS;
else if ("multipleOptions" in definition)
- type = "multiple-options";
+ type = SettingElementType.MULTIPLE_OPTIONS;
else if (typeof definition.default === "number")
- type = "number";
+ type = SettingElementType.NUMBER;
else
- type = "checkbox";
+ type = SettingElementType.CHECKBOX;
const params = Object.assign(overrideParams, definition.params || {});
if (params.disabled)
currentValue = definition.default;
@@ -1069,7 +1207,7 @@ class BaseSettingsStore {
}
}
-function getSupportedCodecProfiles() {
+var getSupportedCodecProfiles = function() {
const options = {
default: t("default")
};
@@ -1104,29 +1242,29 @@ function getSupportedCodecProfiles() {
else
options.low = t("visual-quality-low");
return options;
-}
+};
class GlobalSettingsStorage extends BaseSettingsStore {
static DEFINITIONS = {
- version_last_check: {
+ [PrefKey.LAST_UPDATE_CHECK]: {
default: 0
},
- version_latest: {
+ [PrefKey.LATEST_VERSION]: {
default: ""
},
- version_current: {
+ [PrefKey.CURRENT_VERSION]: {
default: ""
},
- bx_locale: {
+ [PrefKey.BETTER_XCLOUD_LOCALE]: {
label: t("language"),
default: localStorage.getItem("better_xcloud_locale") || "en-US",
options: SUPPORTED_LANGUAGES
},
- server_region: {
+ [PrefKey.SERVER_REGION]: {
label: t("region"),
default: "default"
},
- server_bypass_restriction: {
+ [PrefKey.SERVER_BYPASS_RESTRICTION]: {
label: t("bypass-region-restriction"),
note: "⚠️ " + t("use-this-at-your-own-risk"),
default: "off",
@@ -1135,7 +1273,7 @@ class GlobalSettingsStorage extends BaseSettingsStore {
off: t("off")
}, BypassServers)
},
- stream_preferred_locale: {
+ [PrefKey.STREAM_PREFERRED_LOCALE]: {
label: t("preferred-game-language"),
default: "default",
options: {
@@ -1169,7 +1307,7 @@ class GlobalSettingsStorage extends BaseSettingsStore {
"zh-TW": "中文 (繁體)"
}
},
- stream_target_resolution: {
+ [PrefKey.STREAM_TARGET_RESOLUTION]: {
label: t("target-resolution"),
default: "auto",
options: {
@@ -1178,7 +1316,7 @@ class GlobalSettingsStorage extends BaseSettingsStore {
"720p": "720p"
}
},
- stream_codec_profile: {
+ [PrefKey.STREAM_CODEC_PROFILE]: {
label: t("visual-quality"),
default: "default",
options: getSupportedCodecProfiles(),
@@ -1188,29 +1326,29 @@ class GlobalSettingsStorage extends BaseSettingsStore {
setting.unsupported = !0, setting.note = "⚠️ " + t("browser-unsupported-feature");
}
},
- prefer_ipv6_server: {
+ [PrefKey.PREFER_IPV6_SERVER]: {
label: t("prefer-ipv6-server"),
default: !1
},
- screenshot_apply_filters: {
+ [PrefKey.SCREENSHOT_APPLY_FILTERS]: {
label: t("screenshot-apply-filters"),
default: !1
},
- skip_splash_video: {
+ [PrefKey.SKIP_SPLASH_VIDEO]: {
label: t("skip-splash-video"),
default: !1
},
- hide_dots_icon: {
+ [PrefKey.HIDE_DOTS_ICON]: {
label: t("hide-system-menu-icon"),
default: !1
},
- stream_combine_sources: {
+ [PrefKey.STREAM_COMBINE_SOURCES]: {
label: t("combine-audio-video-streams"),
default: !1,
experimental: !0,
note: t("combine-audio-video-streams-summary")
},
- stream_touch_controller: {
+ [PrefKey.STREAM_TOUCH_CONTROLLER]: {
label: t("tc-availability"),
default: "all",
options: {
@@ -1224,13 +1362,13 @@ class GlobalSettingsStorage extends BaseSettingsStore {
setting.default = "default";
}
},
- stream_touch_controller_auto_off: {
+ [PrefKey.STREAM_TOUCH_CONTROLLER_AUTO_OFF]: {
label: t("tc-auto-off"),
default: !1,
unsupported: !STATES.userAgent.capabilities.touch
},
- stream_touch_controller_default_opacity: {
- type: "number-stepper",
+ [PrefKey.STREAM_TOUCH_CONTROLLER_DEFAULT_OPACITY]: {
+ type: SettingElementType.NUMBER_STEPPER,
label: t("tc-default-opacity"),
default: 100,
min: 10,
@@ -1243,7 +1381,7 @@ class GlobalSettingsStorage extends BaseSettingsStore {
},
unsupported: !STATES.userAgent.capabilities.touch
},
- stream_touch_controller_style_standard: {
+ [PrefKey.STREAM_TOUCH_CONTROLLER_STYLE_STANDARD]: {
label: t("tc-standard-layout-style"),
default: "default",
options: {
@@ -1253,7 +1391,7 @@ class GlobalSettingsStorage extends BaseSettingsStore {
},
unsupported: !STATES.userAgent.capabilities.touch
},
- stream_touch_controller_style_custom: {
+ [PrefKey.STREAM_TOUCH_CONTROLLER_STYLE_CUSTOM]: {
label: t("tc-custom-layout-style"),
default: "default",
options: {
@@ -1262,20 +1400,20 @@ class GlobalSettingsStorage extends BaseSettingsStore {
},
unsupported: !STATES.userAgent.capabilities.touch
},
- stream_simplify_menu: {
+ [PrefKey.STREAM_SIMPLIFY_MENU]: {
label: t("simplify-stream-menu"),
default: !1
},
- mkb_hide_idle_cursor: {
+ [PrefKey.MKB_HIDE_IDLE_CURSOR]: {
label: t("hide-idle-cursor"),
default: !1
},
- stream_disable_feedback_dialog: {
+ [PrefKey.STREAM_DISABLE_FEEDBACK_DIALOG]: {
label: t("disable-post-stream-feedback-dialog"),
default: !1
},
- bitrate_video_max: {
- type: "number-stepper",
+ [PrefKey.BITRATE_VIDEO_MAX]: {
+ type: SettingElementType.NUMBER_STEPPER,
label: t("bitrate-video-maximum"),
note: "⚠️ " + t("unexpected-behavior"),
default: 0,
@@ -1292,7 +1430,7 @@ class GlobalSettingsStorage extends BaseSettingsStore {
}
}
},
- game_bar_position: {
+ [PrefKey.GAME_BAR_POSITION]: {
label: t("position"),
default: "bottom-left",
options: {
@@ -1301,7 +1439,7 @@ class GlobalSettingsStorage extends BaseSettingsStore {
off: t("off")
}
},
- local_co_op_enabled: {
+ [PrefKey.LOCAL_CO_OP_ENABLED]: {
label: t("enable-local-co-op-support"),
default: !1,
note: CE("a", {
@@ -1309,18 +1447,18 @@ class GlobalSettingsStorage extends BaseSettingsStore {
target: "_blank"
}, t("enable-local-co-op-support-note"))
},
- controller_show_connection_status: {
+ [PrefKey.CONTROLLER_SHOW_CONNECTION_STATUS]: {
label: t("show-controller-connection-status"),
default: !0
},
- controller_enable_shortcuts: {
+ [PrefKey.CONTROLLER_ENABLE_SHORTCUTS]: {
default: !1
},
- controller_enable_vibration: {
+ [PrefKey.CONTROLLER_ENABLE_VIBRATION]: {
label: t("controller-vibration"),
default: !0
},
- controller_device_vibration: {
+ [PrefKey.CONTROLLER_DEVICE_VIBRATION]: {
label: t("device-vibration"),
default: "off",
options: {
@@ -1329,9 +1467,9 @@ class GlobalSettingsStorage extends BaseSettingsStore {
off: t("off")
}
},
- controller_vibration_intensity: {
+ [PrefKey.CONTROLLER_VIBRATION_INTENSITY]: {
label: t("vibration-intensity"),
- type: "number-stepper",
+ type: SettingElementType.NUMBER_STEPPER,
default: 100,
min: 0,
max: 100,
@@ -1341,7 +1479,7 @@ class GlobalSettingsStorage extends BaseSettingsStore {
ticks: 10
}
},
- mkb_enabled: {
+ [PrefKey.MKB_ENABLED]: {
label: t("enable-mkb"),
default: !1,
unsupported: (() => {
@@ -1360,7 +1498,7 @@ class GlobalSettingsStorage extends BaseSettingsStore {
}, "⚠️ " + note);
}
},
- native_mkb_enabled: {
+ [PrefKey.NATIVE_MKB_ENABLED]: {
label: t("native-mkb"),
default: "default",
options: {
@@ -1377,9 +1515,9 @@ class GlobalSettingsStorage extends BaseSettingsStore {
delete setting.options.on;
}
},
- native_mkb_scroll_x_sensitivity: {
+ [PrefKey.NATIVE_MKB_SCROLL_HORIZONTAL_SENSITIVITY]: {
label: t("horizontal-scroll-sensitivity"),
- type: "number-stepper",
+ type: SettingElementType.NUMBER_STEPPER,
default: 0,
min: 0,
max: 1e4,
@@ -1393,9 +1531,9 @@ class GlobalSettingsStorage extends BaseSettingsStore {
}
}
},
- native_mkb_scroll_y_sensitivity: {
+ [PrefKey.NATIVE_MKB_SCROLL_VERTICAL_SENSITIVITY]: {
label: t("vertical-scroll-sensitivity"),
- type: "number-stepper",
+ type: SettingElementType.NUMBER_STEPPER,
default: 0,
min: 0,
max: 1e4,
@@ -1409,25 +1547,25 @@ class GlobalSettingsStorage extends BaseSettingsStore {
}
}
},
- mkb_default_preset_id: {
+ [PrefKey.MKB_DEFAULT_PRESET_ID]: {
default: 0
},
- mkb_absolute_mouse: {
+ [PrefKey.MKB_ABSOLUTE_MOUSE]: {
default: !1
},
- reduce_animations: {
+ [PrefKey.REDUCE_ANIMATIONS]: {
label: t("reduce-animations"),
default: !1
},
- ui_loading_screen_game_art: {
+ [PrefKey.UI_LOADING_SCREEN_GAME_ART]: {
label: t("show-game-art"),
default: !0
},
- ui_loading_screen_wait_time: {
+ [PrefKey.UI_LOADING_SCREEN_WAIT_TIME]: {
label: t("show-wait-time"),
default: !0
},
- ui_loading_screen_rocket: {
+ [PrefKey.UI_LOADING_SCREEN_ROCKET]: {
label: t("rocket-animation"),
default: "show",
options: {
@@ -1436,11 +1574,11 @@ class GlobalSettingsStorage extends BaseSettingsStore {
hide: t("rocket-always-hide")
}
},
- ui_controller_friendly: {
+ [PrefKey.UI_CONTROLLER_FRIENDLY]: {
label: t("controller-friendly-ui"),
default: BX_FLAGS.DeviceInfo.deviceType !== "unknown"
},
- ui_layout: {
+ [PrefKey.UI_LAYOUT]: {
label: t("layout"),
default: "default",
options: {
@@ -1449,72 +1587,72 @@ class GlobalSettingsStorage extends BaseSettingsStore {
tv: t("smart-tv")
}
},
- ui_scrollbar_hide: {
+ [PrefKey.UI_SCROLLBAR_HIDE]: {
label: t("hide-scrollbar"),
default: !1
},
- ui_home_context_menu_disabled: {
+ [PrefKey.UI_HOME_CONTEXT_MENU_DISABLED]: {
label: t("disable-home-context-menu"),
default: STATES.browser.capabilities.touch
},
- ui_hide_sections: {
+ [PrefKey.UI_HIDE_SECTIONS]: {
label: t("hide-sections"),
default: [],
multipleOptions: {
- news: t("section-news"),
- friends: t("section-play-with-friends"),
- "native-mkb": t("section-native-mkb"),
- touch: t("section-touch"),
- "most-popular": t("section-most-popular"),
- "all-games": t("section-all-games")
+ [UiSection.NEWS]: t("section-news"),
+ [UiSection.FRIENDS]: t("section-play-with-friends"),
+ [UiSection.NATIVE_MKB]: t("section-native-mkb"),
+ [UiSection.TOUCH]: t("section-touch"),
+ [UiSection.MOST_POPULAR]: t("section-most-popular"),
+ [UiSection.ALL_GAMES]: t("section-all-games")
},
params: {
size: 6
}
},
- ui_game_card_show_wait_time: {
+ [PrefKey.UI_GAME_CARD_SHOW_WAIT_TIME]: {
label: t("show-wait-time-in-game-card"),
default: !1
},
- block_social_features: {
+ [PrefKey.BLOCK_SOCIAL_FEATURES]: {
label: t("disable-social-features"),
default: !1
},
- block_tracking: {
+ [PrefKey.BLOCK_TRACKING]: {
label: t("disable-xcloud-analytics"),
default: !1
},
- user_agent_profile: {
+ [PrefKey.USER_AGENT_PROFILE]: {
label: t("user-agent-profile"),
note: "⚠️ " + t("unexpected-behavior"),
- default: BX_FLAGS.DeviceInfo.deviceType === "android-tv" ? "vr-oculus" : "default",
+ default: BX_FLAGS.DeviceInfo.deviceType === "android-tv" ? UserAgentProfile.VR_OCULUS : "default",
options: {
- default: t("default"),
- "windows-edge": "Edge + Windows",
- "macos-safari": "Safari + macOS",
- "vr-oculus": "Android TV",
- "smarttv-generic": "Smart TV",
- "smarttv-tizen": "Samsung Smart TV",
- custom: t("custom")
+ [UserAgentProfile.DEFAULT]: t("default"),
+ [UserAgentProfile.WINDOWS_EDGE]: "Edge + Windows",
+ [UserAgentProfile.MACOS_SAFARI]: "Safari + macOS",
+ [UserAgentProfile.VR_OCULUS]: "Android TV",
+ [UserAgentProfile.SMART_TV_GENERIC]: "Smart TV",
+ [UserAgentProfile.SMART_TV_TIZEN]: "Samsung Smart TV",
+ [UserAgentProfile.CUSTOM]: t("custom")
}
},
- video_player_type: {
+ [PrefKey.VIDEO_PLAYER_TYPE]: {
label: t("renderer"),
default: "default",
options: {
- default: t("default"),
- webgl2: t("webgl2")
+ [StreamPlayerType.VIDEO]: t("default"),
+ [StreamPlayerType.WEBGL2]: t("webgl2")
}
},
- video_processing: {
+ [PrefKey.VIDEO_PROCESSING]: {
label: t("clarity-boost"),
- default: "usm",
+ default: StreamVideoProcessing.USM,
options: {
- usm: t("unsharp-masking"),
- cas: t("amd-fidelity-cas")
+ [StreamVideoProcessing.USM]: t("unsharp-masking"),
+ [StreamVideoProcessing.CAS]: t("amd-fidelity-cas")
}
},
- video_power_preference: {
+ [PrefKey.VIDEO_POWER_PREFERENCE]: {
label: t("renderer-configuration"),
default: "default",
options: {
@@ -1523,9 +1661,9 @@ class GlobalSettingsStorage extends BaseSettingsStore {
"high-performance": t("high-performance")
}
},
- video_sharpness: {
+ [PrefKey.VIDEO_SHARPNESS]: {
label: t("sharpness"),
- type: "number-stepper",
+ type: SettingElementType.NUMBER_STEPPER,
default: 0,
min: 0,
max: 10,
@@ -1536,7 +1674,7 @@ class GlobalSettingsStorage extends BaseSettingsStore {
}
}
},
- video_ratio: {
+ [PrefKey.VIDEO_RATIO]: {
label: t("aspect-ratio"),
note: t("aspect-ratio-note"),
default: "16:9",
@@ -1549,9 +1687,9 @@ class GlobalSettingsStorage extends BaseSettingsStore {
fill: t("stretch")
}
},
- video_saturation: {
+ [PrefKey.VIDEO_SATURATION]: {
label: t("saturation"),
- type: "number-stepper",
+ type: SettingElementType.NUMBER_STEPPER,
default: 100,
min: 50,
max: 150,
@@ -1560,9 +1698,9 @@ class GlobalSettingsStorage extends BaseSettingsStore {
ticks: 25
}
},
- video_contrast: {
+ [PrefKey.VIDEO_CONTRAST]: {
label: t("contrast"),
- type: "number-stepper",
+ type: SettingElementType.NUMBER_STEPPER,
default: 100,
min: 50,
max: 150,
@@ -1571,9 +1709,9 @@ class GlobalSettingsStorage extends BaseSettingsStore {
ticks: 25
}
},
- video_brightness: {
+ [PrefKey.VIDEO_BRIGHTNESS]: {
label: t("brightness"),
- type: "number-stepper",
+ type: SettingElementType.NUMBER_STEPPER,
default: 100,
min: 50,
max: 150,
@@ -1582,17 +1720,17 @@ class GlobalSettingsStorage extends BaseSettingsStore {
ticks: 25
}
},
- audio_mic_on_playing: {
+ [PrefKey.AUDIO_MIC_ON_PLAYING]: {
label: t("enable-mic-on-startup"),
default: !1
},
- audio_enable_volume_control: {
+ [PrefKey.AUDIO_ENABLE_VOLUME_CONTROL]: {
label: t("enable-volume-control"),
default: !1
},
- audio_volume: {
+ [PrefKey.AUDIO_VOLUME]: {
label: t("volume"),
- type: "number-stepper",
+ type: SettingElementType.NUMBER_STEPPER,
default: 100,
min: 0,
max: 600,
@@ -1602,30 +1740,30 @@ class GlobalSettingsStorage extends BaseSettingsStore {
ticks: 100
}
},
- stats_items: {
+ [PrefKey.STATS_ITEMS]: {
label: t("stats"),
- default: ["ping", "fps", "btr", "dt", "pl", "fl"],
+ default: [StreamStat.PING, StreamStat.FPS, StreamStat.BITRATE, StreamStat.DECODE_TIME, StreamStat.PACKETS_LOST, StreamStat.FRAMES_LOST],
multipleOptions: {
- ping: `${"ping".toUpperCase()}: ${t("stat-ping")}`,
- fps: `${"fps".toUpperCase()}: ${t("stat-fps")}`,
- btr: `${"btr".toUpperCase()}: ${t("stat-bitrate")}`,
- dt: `${"dt".toUpperCase()}: ${t("stat-decode-time")}`,
- pl: `${"pl".toUpperCase()}: ${t("stat-packets-lost")}`,
- fl: `${"fl".toUpperCase()}: ${t("stat-frames-lost")}`
+ [StreamStat.PING]: `${StreamStat.PING.toUpperCase()}: ${t("stat-ping")}`,
+ [StreamStat.FPS]: `${StreamStat.FPS.toUpperCase()}: ${t("stat-fps")}`,
+ [StreamStat.BITRATE]: `${StreamStat.BITRATE.toUpperCase()}: ${t("stat-bitrate")}`,
+ [StreamStat.DECODE_TIME]: `${StreamStat.DECODE_TIME.toUpperCase()}: ${t("stat-decode-time")}`,
+ [StreamStat.PACKETS_LOST]: `${StreamStat.PACKETS_LOST.toUpperCase()}: ${t("stat-packets-lost")}`,
+ [StreamStat.FRAMES_LOST]: `${StreamStat.FRAMES_LOST.toUpperCase()}: ${t("stat-frames-lost")}`
},
params: {
size: 6
}
},
- stats_show_when_playing: {
+ [PrefKey.STATS_SHOW_WHEN_PLAYING]: {
label: t("show-stats-on-startup"),
default: !1
},
- stats_quick_glance: {
+ [PrefKey.STATS_QUICK_GLANCE]: {
label: "👀 " + t("enable-quick-glance-mode"),
default: !0
},
- stats_position: {
+ [PrefKey.STATS_POSITION]: {
label: t("position"),
default: "top-right",
options: {
@@ -1634,7 +1772,7 @@ class GlobalSettingsStorage extends BaseSettingsStore {
"top-right": t("top-right")
}
},
- stats_text_size: {
+ [PrefKey.STATS_TEXT_SIZE]: {
label: t("text-size"),
default: "0.9rem",
options: {
@@ -1643,13 +1781,13 @@ class GlobalSettingsStorage extends BaseSettingsStore {
"1.1rem": t("large")
}
},
- stats_transparent: {
+ [PrefKey.STATS_TRANSPARENT]: {
label: t("transparent-background"),
default: !1
},
- stats_opacity: {
+ [PrefKey.STATS_OPACITY]: {
label: t("opacity"),
- type: "number-stepper",
+ type: SettingElementType.NUMBER_STEPPER,
default: 80,
min: 50,
max: 100,
@@ -1659,29 +1797,29 @@ class GlobalSettingsStorage extends BaseSettingsStore {
ticks: 10
}
},
- stats_conditional_formatting: {
+ [PrefKey.STATS_CONDITIONAL_FORMATTING]: {
label: t("conditional-formatting"),
default: !1
},
- xhome_enabled: {
+ [PrefKey.REMOTE_PLAY_ENABLED]: {
label: t("enable-remote-play-feature"),
default: !1
},
- xhome_resolution: {
+ [PrefKey.REMOTE_PLAY_RESOLUTION]: {
default: "1080p",
options: {
"1080p": "1080p",
"720p": "720p"
}
},
- game_fortnite_force_console: {
+ [PrefKey.GAME_FORTNITE_FORCE_CONSOLE]: {
label: "🎮 " + t("fortnite-force-console-version"),
default: !1,
note: t("fortnite-allow-stw-mode")
}
};
constructor() {
- super("better_xcloud", GlobalSettingsStorage.DEFINITIONS);
+ super(StorageKey.GLOBAL, GlobalSettingsStorage.DEFINITIONS);
}
}
var globalSettings = new GlobalSettingsStorage, getPrefDefinition = globalSettings.getDefinition.bind(globalSettings), getPref = globalSettings.getSetting.bind(globalSettings), setPref = globalSettings.setSetting.bind(globalSettings);
@@ -1690,6 +1828,12 @@ STORAGE.Global = globalSettings;
class Screenshot {
static #$canvas;
static #canvasContext;
+ static #mediaRecorder = null;
+ static #recordedBlobs = [];
+ static #isRecording = !1;
+ static get isRecording() {
+ return this.#isRecording;
+ }
static setup() {
if (Screenshot.#$canvas)
return;
@@ -1714,10 +1858,10 @@ class Screenshot {
if (!streamPlayer || !$canvas)
return;
let $player;
- if (getPref("screenshot_apply_filters"))
+ if (getPref(PrefKey.SCREENSHOT_APPLY_FILTERS))
$player = streamPlayer.getPlayerElement();
else
- $player = streamPlayer.getPlayerElement("default");
+ $player = streamPlayer.getPlayerElement(StreamPlayerType.VIDEO);
if (!$player || !$player.isConnected)
return;
$player.parentElement.addEventListener("animationend", this.#onAnimationEnd, { once: !0 }), $player.parentElement.classList.add("bx-taking-screenshot");
@@ -1737,57 +1881,166 @@ class Screenshot {
$anchor.click(), URL.revokeObjectURL($anchor.href), canvasContext.clearRect(0, 0, $canvas.width, $canvas.height), callback && callback();
}, "image/png");
}
+ static async startRecording() {
+ if (this.#isRecording)
+ return;
+ const $video = STATES.currentStream.streamPlayer?.getPlayerElement();
+ if (!$video)
+ return;
+ const videoStream = $video.captureStream();
+ if (videoStream.getAudioTracks().length === 0)
+ console.warn("No audio tracks found in the video stream");
+ const options = {
+ audioBitsPerSecond: 128000,
+ videoBitsPerSecond: 2500000,
+ mimeType: "video/mp4; codecs=\"avc1.42E01E, mp4a.40.2\""
+ };
+ this.#mediaRecorder = new MediaRecorder(videoStream, options), this.#mediaRecorder.ondataavailable = (event) => {
+ if (event.data.size > 0)
+ this.#recordedBlobs.push(event.data);
+ }, this.#mediaRecorder.onstop = () => {
+ const blob = new Blob(this.#recordedBlobs, { type: "video/mp4" }), url = URL.createObjectURL(blob);
+ CE("a", { download: "recorded-video.mp4", href: url }).click(), URL.revokeObjectURL(url);
+ }, this.#mediaRecorder.start(), this.#isRecording = !0;
+ }
+ static stopRecording() {
+ if (!this.#isRecording)
+ return;
+ this.#mediaRecorder?.stop(), this.#mediaRecorder = null, this.#recordedBlobs = [], this.#isRecording = !1;
+ }
}
+var PrompFont;
+(function(PrompFont2) {
+ PrompFont2["A"] = "⇓";
+ PrompFont2["B"] = "⇒";
+ PrompFont2["X"] = "⇐";
+ PrompFont2["Y"] = "⇑";
+ PrompFont2["LB"] = "↘";
+ PrompFont2["RB"] = "↙";
+ PrompFont2["LT"] = "↖";
+ PrompFont2["RT"] = "↗";
+ PrompFont2["SELECT"] = "⇺";
+ PrompFont2["START"] = "⇻";
+ PrompFont2["HOME"] = "";
+ PrompFont2["UP"] = "≻";
+ PrompFont2["DOWN"] = "≽";
+ PrompFont2["LEFT"] = "≺";
+ PrompFont2["RIGHT"] = "≼";
+ PrompFont2["L3"] = "↺";
+ PrompFont2["LS_UP"] = "↾";
+ PrompFont2["LS_DOWN"] = "⇂";
+ PrompFont2["LS_LEFT"] = "↼";
+ PrompFont2["LS_RIGHT"] = "⇀";
+ PrompFont2["R3"] = "↻";
+ PrompFont2["RS_UP"] = "↿";
+ PrompFont2["RS_DOWN"] = "⇃";
+ PrompFont2["RS_LEFT"] = "↽";
+ PrompFont2["RS_RIGHT"] = "⇁";
+})(PrompFont || (PrompFont = {}));
+
+var GamepadKey;
+(function(GamepadKey2) {
+ GamepadKey2[GamepadKey2["A"] = 0] = "A";
+ GamepadKey2[GamepadKey2["B"] = 1] = "B";
+ GamepadKey2[GamepadKey2["X"] = 2] = "X";
+ GamepadKey2[GamepadKey2["Y"] = 3] = "Y";
+ GamepadKey2[GamepadKey2["LB"] = 4] = "LB";
+ GamepadKey2[GamepadKey2["RB"] = 5] = "RB";
+ GamepadKey2[GamepadKey2["LT"] = 6] = "LT";
+ GamepadKey2[GamepadKey2["RT"] = 7] = "RT";
+ GamepadKey2[GamepadKey2["SELECT"] = 8] = "SELECT";
+ GamepadKey2[GamepadKey2["START"] = 9] = "START";
+ GamepadKey2[GamepadKey2["L3"] = 10] = "L3";
+ GamepadKey2[GamepadKey2["R3"] = 11] = "R3";
+ GamepadKey2[GamepadKey2["UP"] = 12] = "UP";
+ GamepadKey2[GamepadKey2["DOWN"] = 13] = "DOWN";
+ GamepadKey2[GamepadKey2["LEFT"] = 14] = "LEFT";
+ GamepadKey2[GamepadKey2["RIGHT"] = 15] = "RIGHT";
+ GamepadKey2[GamepadKey2["HOME"] = 16] = "HOME";
+ GamepadKey2[GamepadKey2["SHARE"] = 17] = "SHARE";
+ GamepadKey2[GamepadKey2["LS_UP"] = 100] = "LS_UP";
+ GamepadKey2[GamepadKey2["LS_DOWN"] = 101] = "LS_DOWN";
+ GamepadKey2[GamepadKey2["LS_LEFT"] = 102] = "LS_LEFT";
+ GamepadKey2[GamepadKey2["LS_RIGHT"] = 103] = "LS_RIGHT";
+ GamepadKey2[GamepadKey2["RS_UP"] = 200] = "RS_UP";
+ GamepadKey2[GamepadKey2["RS_DOWN"] = 201] = "RS_DOWN";
+ GamepadKey2[GamepadKey2["RS_LEFT"] = 202] = "RS_LEFT";
+ GamepadKey2[GamepadKey2["RS_RIGHT"] = 203] = "RS_RIGHT";
+})(GamepadKey || (GamepadKey = {}));
var GamepadKeyName = {
- [0]: ["A", "⇓"],
- [1]: ["B", "⇒"],
- [2]: ["X", "⇐"],
- [3]: ["Y", "⇑"],
- [4]: ["LB", "↘"],
- [5]: ["RB", "↙"],
- [6]: ["LT", "↖"],
- [7]: ["RT", "↗"],
- [8]: ["Select", "⇺"],
- [9]: ["Start", "⇻"],
- [16]: ["Home", ""],
- [12]: ["D-Pad Up", "≻"],
- [13]: ["D-Pad Down", "≽"],
- [14]: ["D-Pad Left", "≺"],
- [15]: ["D-Pad Right", "≼"],
- [10]: ["L3", "↺"],
- [100]: ["Left Stick Up", "↾"],
- [101]: ["Left Stick Down", "⇂"],
- [102]: ["Left Stick Left", "↼"],
- [103]: ["Left Stick Right", "⇀"],
- [11]: ["R3", "↻"],
- [200]: ["Right Stick Up", "↿"],
- [201]: ["Right Stick Down", "⇃"],
- [202]: ["Right Stick Left", "↽"],
- [203]: ["Right Stick Right", "⇁"]
-};
+ [GamepadKey.A]: ["A", PrompFont.A],
+ [GamepadKey.B]: ["B", PrompFont.B],
+ [GamepadKey.X]: ["X", PrompFont.X],
+ [GamepadKey.Y]: ["Y", PrompFont.Y],
+ [GamepadKey.LB]: ["LB", PrompFont.LB],
+ [GamepadKey.RB]: ["RB", PrompFont.RB],
+ [GamepadKey.LT]: ["LT", PrompFont.LT],
+ [GamepadKey.RT]: ["RT", PrompFont.RT],
+ [GamepadKey.SELECT]: ["Select", PrompFont.SELECT],
+ [GamepadKey.START]: ["Start", PrompFont.START],
+ [GamepadKey.HOME]: ["Home", PrompFont.HOME],
+ [GamepadKey.UP]: ["D-Pad Up", PrompFont.UP],
+ [GamepadKey.DOWN]: ["D-Pad Down", PrompFont.DOWN],
+ [GamepadKey.LEFT]: ["D-Pad Left", PrompFont.LEFT],
+ [GamepadKey.RIGHT]: ["D-Pad Right", PrompFont.RIGHT],
+ [GamepadKey.L3]: ["L3", PrompFont.L3],
+ [GamepadKey.LS_UP]: ["Left Stick Up", PrompFont.LS_UP],
+ [GamepadKey.LS_DOWN]: ["Left Stick Down", PrompFont.LS_DOWN],
+ [GamepadKey.LS_LEFT]: ["Left Stick Left", PrompFont.LS_LEFT],
+ [GamepadKey.LS_RIGHT]: ["Left Stick Right", PrompFont.LS_RIGHT],
+ [GamepadKey.R3]: ["R3", PrompFont.R3],
+ [GamepadKey.RS_UP]: ["Right Stick Up", PrompFont.RS_UP],
+ [GamepadKey.RS_DOWN]: ["Right Stick Down", PrompFont.RS_DOWN],
+ [GamepadKey.RS_LEFT]: ["Right Stick Left", PrompFont.RS_LEFT],
+ [GamepadKey.RS_RIGHT]: ["Right Stick Right", PrompFont.RS_RIGHT]
+}, GamepadStick;
+(function(GamepadStick2) {
+ GamepadStick2[GamepadStick2["LEFT"] = 0] = "LEFT";
+ GamepadStick2[GamepadStick2["RIGHT"] = 1] = "RIGHT";
+})(GamepadStick || (GamepadStick = {}));
+var MouseButtonCode;
+(function(MouseButtonCode2) {
+ MouseButtonCode2["LEFT_CLICK"] = "Mouse0";
+ MouseButtonCode2["RIGHT_CLICK"] = "Mouse2";
+ MouseButtonCode2["MIDDLE_CLICK"] = "Mouse1";
+})(MouseButtonCode || (MouseButtonCode = {}));
var MouseMapTo;
-((MouseMapTo2) => {
- MouseMapTo2[MouseMapTo2.OFF = 0] = "OFF";
- MouseMapTo2[MouseMapTo2.LS = 1] = "LS";
- MouseMapTo2[MouseMapTo2.RS = 2] = "RS";
-})(MouseMapTo ||= {});
+(function(MouseMapTo2) {
+ MouseMapTo2[MouseMapTo2["OFF"] = 0] = "OFF";
+ MouseMapTo2[MouseMapTo2["LS"] = 1] = "LS";
+ MouseMapTo2[MouseMapTo2["RS"] = 2] = "RS";
+})(MouseMapTo || (MouseMapTo = {}));
+var WheelCode;
+(function(WheelCode2) {
+ WheelCode2["SCROLL_UP"] = "ScrollUp";
+ WheelCode2["SCROLL_DOWN"] = "ScrollDown";
+ WheelCode2["SCROLL_LEFT"] = "ScrollLeft";
+ WheelCode2["SCROLL_RIGHT"] = "ScrollRight";
+})(WheelCode || (WheelCode = {}));
+var MkbPresetKey;
+(function(MkbPresetKey2) {
+ MkbPresetKey2["MOUSE_MAP_TO"] = "map_to";
+ MkbPresetKey2["MOUSE_SENSITIVITY_X"] = "sensitivity_x";
+ MkbPresetKey2["MOUSE_SENSITIVITY_Y"] = "sensitivity_y";
+ MkbPresetKey2["MOUSE_DEADZONE_COUNTERWEIGHT"] = "deadzone_counterweight";
+})(MkbPresetKey || (MkbPresetKey = {}));
class MkbPreset {
static MOUSE_SETTINGS = {
- map_to: {
+ [MkbPresetKey.MOUSE_MAP_TO]: {
label: t("map-mouse-to"),
- type: "options",
- default: MouseMapTo[2],
+ type: SettingElementType.OPTIONS,
+ default: MouseMapTo[MouseMapTo.RS],
options: {
- [MouseMapTo[2]]: t("right-stick"),
- [MouseMapTo[1]]: t("left-stick"),
- [MouseMapTo[0]]: t("off")
+ [MouseMapTo[MouseMapTo.RS]]: t("right-stick"),
+ [MouseMapTo[MouseMapTo.LS]]: t("left-stick"),
+ [MouseMapTo[MouseMapTo.OFF]]: t("off")
}
},
- sensitivity_y: {
+ [MkbPresetKey.MOUSE_SENSITIVITY_Y]: {
label: t("horizontal-sensitivity"),
- type: "number-stepper",
+ type: SettingElementType.NUMBER_STEPPER,
default: 50,
min: 1,
max: 300,
@@ -1796,9 +2049,9 @@ class MkbPreset {
exactTicks: 50
}
},
- sensitivity_x: {
+ [MkbPresetKey.MOUSE_SENSITIVITY_X]: {
label: t("vertical-sensitivity"),
- type: "number-stepper",
+ type: SettingElementType.NUMBER_STEPPER,
default: 50,
min: 1,
max: 300,
@@ -1807,9 +2060,9 @@ class MkbPreset {
exactTicks: 50
}
},
- deadzone_counterweight: {
+ [MkbPresetKey.MOUSE_DEADZONE_COUNTERWEIGHT]: {
label: t("deadzone-counterweight"),
- type: "number-stepper",
+ type: SettingElementType.NUMBER_STEPPER,
default: 20,
min: 1,
max: 50,
@@ -1821,37 +2074,37 @@ class MkbPreset {
};
static DEFAULT_PRESET = {
mapping: {
- 12: ["ArrowUp"],
- 13: ["ArrowDown"],
- 14: ["ArrowLeft"],
- 15: ["ArrowRight"],
- 100: ["KeyW"],
- 101: ["KeyS"],
- 102: ["KeyA"],
- 103: ["KeyD"],
- 200: ["KeyI"],
- 201: ["KeyK"],
- 202: ["KeyJ"],
- 203: ["KeyL"],
- 0: ["Space", "KeyE"],
- 2: ["KeyR"],
- 1: ["ControlLeft", "Backspace"],
- 3: ["KeyV"],
- 9: ["Enter"],
- 8: ["Tab"],
- 4: ["KeyC", "KeyG"],
- 5: ["KeyQ"],
- 16: ["Backquote"],
- 7: ["Mouse0"],
- 6: ["Mouse2"],
- 10: ["ShiftLeft"],
- 11: ["KeyF"]
+ [GamepadKey.UP]: ["ArrowUp"],
+ [GamepadKey.DOWN]: ["ArrowDown"],
+ [GamepadKey.LEFT]: ["ArrowLeft"],
+ [GamepadKey.RIGHT]: ["ArrowRight"],
+ [GamepadKey.LS_UP]: ["KeyW"],
+ [GamepadKey.LS_DOWN]: ["KeyS"],
+ [GamepadKey.LS_LEFT]: ["KeyA"],
+ [GamepadKey.LS_RIGHT]: ["KeyD"],
+ [GamepadKey.RS_UP]: ["KeyI"],
+ [GamepadKey.RS_DOWN]: ["KeyK"],
+ [GamepadKey.RS_LEFT]: ["KeyJ"],
+ [GamepadKey.RS_RIGHT]: ["KeyL"],
+ [GamepadKey.A]: ["Space", "KeyE"],
+ [GamepadKey.X]: ["KeyR"],
+ [GamepadKey.B]: ["ControlLeft", "Backspace"],
+ [GamepadKey.Y]: ["KeyV"],
+ [GamepadKey.START]: ["Enter"],
+ [GamepadKey.SELECT]: ["Tab"],
+ [GamepadKey.LB]: ["KeyC", "KeyG"],
+ [GamepadKey.RB]: ["KeyQ"],
+ [GamepadKey.HOME]: ["Backquote"],
+ [GamepadKey.RT]: [MouseButtonCode.LEFT_CLICK],
+ [GamepadKey.LT]: [MouseButtonCode.RIGHT_CLICK],
+ [GamepadKey.L3]: ["ShiftLeft"],
+ [GamepadKey.R3]: ["KeyF"]
},
mouse: {
- map_to: MouseMapTo[2],
- sensitivity_x: 100,
- sensitivity_y: 100,
- deadzone_counterweight: 20
+ [MkbPresetKey.MOUSE_MAP_TO]: MouseMapTo[MouseMapTo.RS],
+ [MkbPresetKey.MOUSE_SENSITIVITY_X]: 100,
+ [MkbPresetKey.MOUSE_SENSITIVITY_Y]: 100,
+ [MkbPresetKey.MOUSE_DEADZONE_COUNTERWEIGHT]: 20
}
};
static convert(preset) {
@@ -1863,12 +2116,12 @@ class MkbPreset {
for (let keyName of preset.mapping[parseInt(buttonIndex)])
obj.mapping[keyName] = parseInt(buttonIndex);
const mouse = obj.mouse;
- mouse["sensitivity_x"] *= EmulatedMkbHandler.DEFAULT_PANNING_SENSITIVITY, mouse["sensitivity_y"] *= EmulatedMkbHandler.DEFAULT_PANNING_SENSITIVITY, mouse["deadzone_counterweight"] *= EmulatedMkbHandler.DEFAULT_DEADZONE_COUNTERWEIGHT;
- const mouseMapTo = MouseMapTo[mouse["map_to"]];
+ mouse[MkbPresetKey.MOUSE_SENSITIVITY_X] *= EmulatedMkbHandler.DEFAULT_PANNING_SENSITIVITY, mouse[MkbPresetKey.MOUSE_SENSITIVITY_Y] *= EmulatedMkbHandler.DEFAULT_PANNING_SENSITIVITY, mouse[MkbPresetKey.MOUSE_DEADZONE_COUNTERWEIGHT] *= EmulatedMkbHandler.DEFAULT_DEADZONE_COUNTERWEIGHT;
+ const mouseMapTo = MouseMapTo[mouse[MkbPresetKey.MOUSE_MAP_TO]];
if (typeof mouseMapTo !== "undefined")
- mouse["map_to"] = mouseMapTo;
+ mouse[MkbPresetKey.MOUSE_MAP_TO] = mouseMapTo;
else
- mouse["map_to"] = MkbPreset.MOUSE_SETTINGS["map_to"].default;
+ mouse[MkbPresetKey.MOUSE_MAP_TO] = MkbPreset.MOUSE_SETTINGS[MkbPresetKey.MOUSE_MAP_TO].default;
return console.log(obj), obj;
}
}
@@ -2012,7 +2265,7 @@ class LocalDb {
};
return new Promise((resolve) => {
this.#add(table, preset).then(([table2, id2]) => {
- preset.id = id2, setPref("mkb_default_preset_id", id2), resolve({ [id2]: preset });
+ preset.id = id2, setPref(PrefKey.MKB_DEFAULT_PRESET_ID, id2), resolve({ [id2]: preset });
});
});
});
@@ -2022,13 +2275,13 @@ class LocalDb {
class KeyHelper {
static #NON_PRINTABLE_KEYS = {
Backquote: "`",
- Mouse0: "Left Click",
- Mouse2: "Right Click",
- Mouse1: "Middle Click",
- ScrollUp: "Scroll Up",
- ScrollDown: "Scroll Down",
- ScrollLeft: "Scroll Left",
- ScrollRight: "Scroll Right"
+ [MouseButtonCode.LEFT_CLICK]: "Left Click",
+ [MouseButtonCode.RIGHT_CLICK]: "Right Click",
+ [MouseButtonCode.MIDDLE_CLICK]: "Middle Click",
+ [WheelCode.SCROLL_UP]: "Scroll Up",
+ [WheelCode.SCROLL_DOWN]: "Scroll Down",
+ [WheelCode.SCROLL_LEFT]: "Scroll Left",
+ [WheelCode.SCROLL_RIGHT]: "Scroll Right"
};
static getKeyFromEvent(e) {
let code, name;
@@ -2036,13 +2289,13 @@ class KeyHelper {
code = e.code || e.key;
else if (e instanceof WheelEvent) {
if (e.deltaY < 0)
- code = "ScrollUp";
+ code = WheelCode.SCROLL_UP;
else if (e.deltaY > 0)
- code = "ScrollDown";
+ code = WheelCode.SCROLL_DOWN;
else if (e.deltaX < 0)
- code = "ScrollLeft";
+ code = WheelCode.SCROLL_LEFT;
else if (e.deltaX > 0)
- code = "ScrollRight";
+ code = WheelCode.SCROLL_RIGHT;
} else if (e instanceof MouseEvent)
code = "Mouse" + e.button;
if (code)
@@ -2054,7 +2307,15 @@ class KeyHelper {
}
}
-var LOG_TAG = "PointerClient";
+var LOG_TAG = "PointerClient", PointerAction;
+(function(PointerAction2) {
+ PointerAction2[PointerAction2["MOVE"] = 1] = "MOVE";
+ PointerAction2[PointerAction2["BUTTON_PRESS"] = 2] = "BUTTON_PRESS";
+ PointerAction2[PointerAction2["BUTTON_RELEASE"] = 3] = "BUTTON_RELEASE";
+ PointerAction2[PointerAction2["SCROLL"] = 4] = "SCROLL";
+ PointerAction2[PointerAction2["POINTER_CAPTURE_CHANGED"] = 5] = "POINTER_CAPTURE_CHANGED";
+})(PointerAction || (PointerAction = {}));
+
class PointerClient {
static instance;
static getInstance() {
@@ -2077,17 +2338,17 @@ class PointerClient {
const dataView = new DataView(event.data);
let messageType = dataView.getInt8(0), offset = Int8Array.BYTES_PER_ELEMENT;
switch (messageType) {
- case 1:
+ case PointerAction.MOVE:
this.onMove(dataView, offset);
break;
- case 2:
- case 3:
+ case PointerAction.BUTTON_PRESS:
+ case PointerAction.BUTTON_RELEASE:
this.onPress(messageType, dataView, offset);
break;
- case 4:
+ case PointerAction.SCROLL:
this.onScroll(dataView, offset);
break;
- case 5:
+ case PointerAction.POINTER_CAPTURE_CHANGED:
this.onPointerCaptureChanged(dataView, offset);
}
});
@@ -2105,7 +2366,7 @@ class PointerClient {
const button = dataView.getUint8(offset);
this.#mkbHandler?.handleMouseClick({
pointerButton: button,
- pressed: messageType === 2
+ pressed: messageType === PointerAction.BUTTON_PRESS
});
}
onScroll(dataView, offset) {
@@ -2140,6 +2401,9 @@ class MkbHandler {
}
class NativeMkbHandler extends MkbHandler {
+ constructor() {
+ super(...arguments);
+ }
static instance;
#pointerClient;
#enabled = !1;
@@ -2222,7 +2486,7 @@ class NativeMkbHandler extends MkbHandler {
} catch (e) {
Toast.show("Cannot enable Mouse & Keyboard feature");
}
- if (this.#mouseVerticalMultiply = getPref("native_mkb_scroll_y_sensitivity"), this.#mouseHorizontalMultiply = getPref("native_mkb_scroll_x_sensitivity"), window.addEventListener("keyup", this), window.addEventListener(BxEvent.XCLOUD_DIALOG_SHOWN, this), window.addEventListener(BxEvent.POINTER_LOCK_REQUESTED, this), window.addEventListener(BxEvent.POINTER_LOCK_EXITED, this), window.addEventListener(BxEvent.XCLOUD_POLLING_MODE_CHANGED, this), this.#initMessage(), AppInterface)
+ if (this.#mouseVerticalMultiply = getPref(PrefKey.NATIVE_MKB_SCROLL_VERTICAL_SENSITIVITY), this.#mouseHorizontalMultiply = getPref(PrefKey.NATIVE_MKB_SCROLL_HORIZONTAL_SENSITIVITY), window.addEventListener("keyup", this), window.addEventListener(BxEvent.XCLOUD_DIALOG_SHOWN, this), window.addEventListener(BxEvent.POINTER_LOCK_REQUESTED, this), window.addEventListener(BxEvent.POINTER_LOCK_EXITED, this), window.addEventListener(BxEvent.XCLOUD_POLLING_MODE_CHANGED, this), this.#initMessage(), AppInterface)
Toast.show(t("press-key-to-toggle-mkb", { key: "F8" }), t("native-mkb"), { html: !0 }), this.#$message?.classList.add("bx-gone");
else
this.#$message?.classList.remove("bx-gone");
@@ -2318,32 +2582,40 @@ class NativeMkbHandler extends MkbHandler {
}
function onChangeVideoPlayerType() {
- const playerType = getPref("video_player_type"), $videoProcessing = document.getElementById("bx_setting_video_processing"), $videoSharpness = document.getElementById("bx_setting_video_sharpness"), $videoPowerPreference = document.getElementById("bx_setting_video_power_preference");
+ const playerType = getPref(PrefKey.VIDEO_PLAYER_TYPE), $videoProcessing = document.getElementById("bx_setting_video_processing"), $videoSharpness = document.getElementById("bx_setting_video_sharpness"), $videoPowerPreference = document.getElementById("bx_setting_video_power_preference");
if (!$videoProcessing)
return;
let isDisabled = !1;
- const $optCas = $videoProcessing.querySelector(`option[value=${"cas"}]`);
- if (playerType === "webgl2")
+ const $optCas = $videoProcessing.querySelector(`option[value=${StreamVideoProcessing.CAS}]`);
+ if (playerType === StreamPlayerType.WEBGL2)
$optCas && ($optCas.disabled = !1);
- else if ($videoProcessing.value = "usm", setPref("video_processing", "usm"), $optCas && ($optCas.disabled = !0), UserAgent.isSafari())
+ else if ($videoProcessing.value = StreamVideoProcessing.USM, setPref(PrefKey.VIDEO_PROCESSING, StreamVideoProcessing.USM), $optCas && ($optCas.disabled = !0), UserAgent.isSafari())
isDisabled = !0;
- $videoProcessing.disabled = isDisabled, $videoSharpness.dataset.disabled = isDisabled.toString(), $videoPowerPreference.closest(".bx-settings-row").classList.toggle("bx-gone", playerType !== "webgl2"), updateVideoPlayer();
+ $videoProcessing.disabled = isDisabled, $videoSharpness.dataset.disabled = isDisabled.toString(), $videoPowerPreference.closest(".bx-settings-row").classList.toggle("bx-gone", playerType !== StreamPlayerType.WEBGL2), updateVideoPlayer();
}
function updateVideoPlayer() {
const streamPlayer = STATES.currentStream.streamPlayer;
if (!streamPlayer)
return;
const options = {
- processing: getPref("video_processing"),
- sharpness: getPref("video_sharpness"),
- saturation: getPref("video_saturation"),
- contrast: getPref("video_contrast"),
- brightness: getPref("video_brightness")
+ processing: getPref(PrefKey.VIDEO_PROCESSING),
+ sharpness: getPref(PrefKey.VIDEO_SHARPNESS),
+ saturation: getPref(PrefKey.VIDEO_SATURATION),
+ contrast: getPref(PrefKey.VIDEO_CONTRAST),
+ brightness: getPref(PrefKey.VIDEO_BRIGHTNESS)
};
- streamPlayer.setPlayerType(getPref("video_player_type")), streamPlayer.updateOptions(options), streamPlayer.refreshPlayer();
+ streamPlayer.setPlayerType(getPref(PrefKey.VIDEO_PLAYER_TYPE)), streamPlayer.updateOptions(options), streamPlayer.refreshPlayer();
}
window.addEventListener("resize", updateVideoPlayer);
+var NavigationDirection;
+(function(NavigationDirection2) {
+ NavigationDirection2[NavigationDirection2["UP"] = 1] = "UP";
+ NavigationDirection2[NavigationDirection2["RIGHT"] = 2] = "RIGHT";
+ NavigationDirection2[NavigationDirection2["DOWN"] = 3] = "DOWN";
+ NavigationDirection2[NavigationDirection2["LEFT"] = 4] = "LEFT";
+})(NavigationDirection || (NavigationDirection = {}));
+
class NavigationDialog {
dialogManager;
constructor() {
@@ -2389,35 +2661,35 @@ class NavigationDialogManager {
}
static GAMEPAD_POLLING_INTERVAL = 50;
static GAMEPAD_KEYS = [
- 12,
- 13,
- 14,
- 15,
- 0,
- 1,
- 4,
- 5,
- 6,
- 7
+ GamepadKey.UP,
+ GamepadKey.DOWN,
+ GamepadKey.LEFT,
+ GamepadKey.RIGHT,
+ GamepadKey.A,
+ GamepadKey.B,
+ GamepadKey.LB,
+ GamepadKey.RB,
+ GamepadKey.LT,
+ GamepadKey.RT
];
static GAMEPAD_DIRECTION_MAP = {
- 12: 1,
- 13: 3,
- 14: 4,
- 15: 2,
- 100: 1,
- 101: 3,
- 102: 4,
- 103: 2
+ [GamepadKey.UP]: NavigationDirection.UP,
+ [GamepadKey.DOWN]: NavigationDirection.DOWN,
+ [GamepadKey.LEFT]: NavigationDirection.LEFT,
+ [GamepadKey.RIGHT]: NavigationDirection.RIGHT,
+ [GamepadKey.LS_UP]: NavigationDirection.UP,
+ [GamepadKey.LS_DOWN]: NavigationDirection.DOWN,
+ [GamepadKey.LS_LEFT]: NavigationDirection.LEFT,
+ [GamepadKey.LS_RIGHT]: NavigationDirection.RIGHT
};
static SIBLING_PROPERTY_MAP = {
horizontal: {
- [4]: "previousElementSibling",
- [2]: "nextElementSibling"
+ [NavigationDirection.LEFT]: "previousElementSibling",
+ [NavigationDirection.RIGHT]: "nextElementSibling"
},
vertical: {
- [1]: "previousElementSibling",
- [3]: "nextElementSibling"
+ [NavigationDirection.UP]: "previousElementSibling",
+ [NavigationDirection.DOWN]: "nextElementSibling"
}
};
gamepadPollingIntervalId = null;
@@ -2441,10 +2713,10 @@ class NavigationDialogManager {
return;
}
if (keyCode === "ArrowUp" || keyCode === "ArrowDown")
- handled = !0, this.focusDirection(keyCode === "ArrowUp" ? 1 : 3);
+ handled = !0, this.focusDirection(keyCode === "ArrowUp" ? NavigationDirection.UP : NavigationDirection.DOWN);
else if (keyCode === "ArrowLeft" || keyCode === "ArrowRight") {
if (!($target instanceof HTMLInputElement && ($target.type === "text" || $target.type === "range")))
- handled = !0, this.focusDirection(keyCode === "ArrowLeft" ? 4 : 2);
+ handled = !0, this.focusDirection(keyCode === "ArrowLeft" ? NavigationDirection.LEFT : NavigationDirection.RIGHT);
} else if (keyCode === "Enter" || keyCode === "NumpadEnter" || keyCode === "Space") {
if (!($target instanceof HTMLInputElement && $target.type === "text"))
handled = !0, $target.dispatchEvent(new MouseEvent("click"));
@@ -2481,19 +2753,19 @@ class NavigationDialogManager {
}
if (heldButton === null && releasedButton === null && axes && axes.length >= 2) {
if (lastKey) {
- const releasedHorizontal = Math.abs(axes[0]) < 0.1 && (lastKey === 102 || lastKey === 103), releasedVertical = Math.abs(axes[1]) < 0.1 && (lastKey === 100 || lastKey === 101);
+ const releasedHorizontal = Math.abs(axes[0]) < 0.1 && (lastKey === GamepadKey.LS_LEFT || lastKey === GamepadKey.LS_RIGHT), releasedVertical = Math.abs(axes[1]) < 0.1 && (lastKey === GamepadKey.LS_UP || lastKey === GamepadKey.LS_DOWN);
if (releasedHorizontal || releasedVertical)
releasedButton = lastKey;
else
heldButton = lastKey;
} else if (axes[0] < -0.5)
- heldButton = 102;
+ heldButton = GamepadKey.LS_LEFT;
else if (axes[0] > 0.5)
- heldButton = 103;
+ heldButton = GamepadKey.LS_RIGHT;
else if (axes[1] < -0.5)
- heldButton = 100;
+ heldButton = GamepadKey.LS_UP;
else if (axes[1] > 0.5)
- heldButton = 101;
+ heldButton = GamepadKey.LS_DOWN;
}
if (heldButton !== null) {
if (this.gamepadLastStates[gamepad.index] = [gamepad.timestamp, heldButton, !1], this.clearGamepadHoldingInterval(), NavigationDialogManager.GAMEPAD_DIRECTION_MAP[heldButton])
@@ -2515,10 +2787,10 @@ class NavigationDialogManager {
}
if (this.gamepadLastStates[gamepad.index] = null, lastKeyPressed)
return;
- if (releasedButton === 0) {
+ if (releasedButton === GamepadKey.A) {
document.activeElement && document.activeElement.dispatchEvent(new MouseEvent("click"));
return;
- } else if (releasedButton === 1) {
+ } else if (releasedButton === GamepadKey.B) {
this.hide();
return;
}
@@ -2535,8 +2807,8 @@ class NavigationDialogManager {
return !1;
if (document.activeElement instanceof HTMLInputElement && document.activeElement.type === "range") {
const $range = document.activeElement;
- if (direction === 4 || direction === 2)
- $range.value = (parseInt($range.value) + parseInt($range.step) * (direction === 4 ? -1 : 1)).toString(), $range.dispatchEvent(new InputEvent("input")), handled = !0;
+ if (direction === NavigationDirection.LEFT || direction === NavigationDirection.RIGHT)
+ $range.value = (parseInt($range.value) + parseInt($range.step) * (direction === NavigationDirection.LEFT ? -1 : 1)).toString(), $range.dispatchEvent(new InputEvent("input")), handled = !0;
}
if (!handled)
this.focusDirection(direction);
@@ -2626,7 +2898,7 @@ class NavigationDialogManager {
}
}
const children = Array.from($elm.children), orientation = $elm.nearby?.orientation;
- if (orientation === "horizontal" || orientation === "vertical" && direction === 1)
+ if (orientation === "horizontal" || orientation === "vertical" && direction === NavigationDirection.UP)
children.reverse();
for (let $child of children) {
if (!$child || !($child instanceof HTMLElement))
@@ -2659,67 +2931,69 @@ class NavigationDialogManager {
}
}
-var better_xcloud_default = "\n";
+var better_xcloud_default = "\r\n";
-var close_default = "\n";
+var close_default = "\r\n";
-var command_default = "\n";
+var command_default = "\r\n";
-var controller_default = "\n";
+var controller_default = "\r\n";
-var copy_default = "\n";
+var copy_default = "\r\n";
-var create_shortcut_default = "\n";
+var create_shortcut_default = "\r\n";
-var cursor_text_default = "\n";
+var cursor_text_default = "\r\n";
-var display_default = "\n";
+var display_default = "\r\n";
-var home_default = "\n";
+var home_default = "\r\n";
-var native_mkb_default = "\n";
+var native_mkb_default = "\r\n";
-var new_default = "\n";
+var new_default = "\r\n";
-var question_default = "\n";
+var question_default = "\r\n";
-var refresh_default = "\n";
+var refresh_default = "\r\n";
-var remote_play_default = "\n";
+var remote_play_default = "\r\n";
-var stream_settings_default = "\n";
+var stream_settings_default = "\r\n";
-var stream_stats_default = "\n";
+var stream_stats_default = "\r\n";
-var touch_control_disable_default = "\n";
+var touch_control_disable_default = "\r\n";
-var touch_control_enable_default = "\n";
+var touch_control_enable_default = "\r\n";
-var trash_default = "\n";
+var trash_default = "\r\n";
-var virtual_controller_default = "\n";
+var virtual_controller_default = "\r\n";
-var caret_left_default = "\n";
+var caret_left_default = "\r\n";
-var caret_right_default = "\n";
+var caret_right_default = "\r\n";
-var camera_default = "\n";
+var camera_default = "\r\n";
-var microphone_default = "\n";
+var camera_record_default = "\n";
-var microphone_slash_default = "\n";
+var microphone_default = "\r\n";
-var battery_full_default = "\n";
+var microphone_slash_default = "\r\n";
-var clock_default = "\n";
+var battery_full_default = "\r\n";
-var cloud_default = "\n";
+var clock_default = "\r\n";
-var download_default = "\n";
+var cloud_default = "\r\n";
-var speaker_high_default = "\n";
+var download_default = "\r\n";
-var upload_default = "\n";
+var speaker_high_default = "\r\n";
+
+var upload_default = "\r\n";
var BxIcon = {
BETTER_XCLOUD: better_xcloud_default,
@@ -2743,6 +3017,7 @@ var BxIcon = {
CARET_LEFT: caret_left_default,
CARET_RIGHT: caret_right_default,
SCREENSHOT: camera_default,
+ RECORD: camera_record_default,
TOUCH_CONTROL_ENABLE: touch_control_enable_default,
TOUCH_CONTROL_DISABLE: touch_control_disable_default,
MICROPHONE: microphone_default,
@@ -2799,31 +3074,31 @@ class Dialog {
class MkbRemapper {
#BUTTON_ORDERS = [
- 12,
- 13,
- 14,
- 15,
- 0,
- 1,
- 2,
- 3,
- 4,
- 5,
- 6,
- 7,
- 8,
- 9,
- 16,
- 10,
- 100,
- 101,
- 102,
- 103,
- 11,
- 200,
- 201,
- 202,
- 203
+ GamepadKey.UP,
+ GamepadKey.DOWN,
+ GamepadKey.LEFT,
+ GamepadKey.RIGHT,
+ GamepadKey.A,
+ GamepadKey.B,
+ GamepadKey.X,
+ GamepadKey.Y,
+ GamepadKey.LB,
+ GamepadKey.RB,
+ GamepadKey.LT,
+ GamepadKey.RT,
+ GamepadKey.SELECT,
+ GamepadKey.START,
+ GamepadKey.HOME,
+ GamepadKey.L3,
+ GamepadKey.LS_UP,
+ GamepadKey.LS_DOWN,
+ GamepadKey.LS_LEFT,
+ GamepadKey.LS_RIGHT,
+ GamepadKey.R3,
+ GamepadKey.RS_UP,
+ GamepadKey.RS_DOWN,
+ GamepadKey.RS_LEFT,
+ GamepadKey.RS_RIGHT
];
static #instance;
static get INSTANCE() {
@@ -2847,7 +3122,7 @@ class MkbRemapper {
};
bindingDialog;
constructor() {
- this.#STATE.currentPresetId = getPref("mkb_default_preset_id"), this.bindingDialog = new Dialog({
+ this.#STATE.currentPresetId = getPref(PrefKey.MKB_DEFAULT_PRESET_ID), this.bindingDialog = new Dialog({
className: "bx-binding-dialog",
content: CE("div", {}, CE("p", {}, t("press-to-bind")), CE("i", {}, t("press-esc-to-cancel"))),
hideCloseButton: !0
@@ -2914,7 +3189,7 @@ class MkbRemapper {
value = MkbPreset.MOUSE_SETTINGS[key].default;
"setValue" in $elm && $elm.setValue(value);
}
- const activated = getPref("mkb_default_preset_id") === this.#STATE.currentPresetId;
+ const activated = getPref(PrefKey.MKB_DEFAULT_PRESET_ID) === this.#STATE.currentPresetId;
this.#$.activateButton.disabled = activated, this.#$.activateButton.querySelector("span").textContent = activated ? t("activated") : t("activate");
};
#refresh() {
@@ -2925,9 +3200,9 @@ class MkbRemapper {
const $fragment = document.createDocumentFragment();
let defaultPresetId;
if (this.#STATE.currentPresetId === 0)
- this.#STATE.currentPresetId = parseInt(Object.keys(presets)[0]), defaultPresetId = this.#STATE.currentPresetId, setPref("mkb_default_preset_id", defaultPresetId), EmulatedMkbHandler.getInstance().refreshPresetData();
+ this.#STATE.currentPresetId = parseInt(Object.keys(presets)[0]), defaultPresetId = this.#STATE.currentPresetId, setPref(PrefKey.MKB_DEFAULT_PRESET_ID, defaultPresetId), EmulatedMkbHandler.getInstance().refreshPresetData();
else
- defaultPresetId = getPref("mkb_default_preset_id");
+ defaultPresetId = getPref(PrefKey.MKB_DEFAULT_PRESET_ID);
for (let id2 in presets) {
let name = presets[id2].name;
if (id2 === defaultPresetId)
@@ -3055,7 +3330,7 @@ class MkbRemapper {
style: ButtonStyle.PRIMARY,
tabIndex: -1,
onClick: (e) => {
- setPref("mkb_default_preset_id", this.#STATE.currentPresetId), EmulatedMkbHandler.getInstance().refreshPresetData(), this.#refresh();
+ setPref(PrefKey.MKB_DEFAULT_PRESET_ID, this.#STATE.currentPresetId), EmulatedMkbHandler.getInstance().refreshPresetData(), this.#refresh();
}
})), CE("div", {}, createButton({
label: t("cancel"),
@@ -3071,7 +3346,7 @@ class MkbRemapper {
onClick: (e) => {
const updatedPreset = deepClone(this.#getCurrentPreset());
updatedPreset.data = this.#STATE.editingPresetData, LocalDb.INSTANCE.updatePreset(updatedPreset).then((id2) => {
- if (id2 === getPref("mkb_default_preset_id"))
+ if (id2 === getPref(PrefKey.MKB_DEFAULT_PRESET_ID))
EmulatedMkbHandler.getInstance().refreshPresetData();
this.#toggleEditing(!1), this.#refresh();
});
@@ -3084,11 +3359,11 @@ class MkbRemapper {
function checkForUpdate() {
if (SCRIPT_VERSION.includes("beta"))
return;
- const CHECK_INTERVAL_SECONDS = 7200, currentVersion = getPref("version_current"), lastCheck = getPref("version_last_check"), now = Math.round(+new Date / 1000);
+ const CHECK_INTERVAL_SECONDS = 7200, currentVersion = getPref(PrefKey.CURRENT_VERSION), lastCheck = getPref(PrefKey.LAST_UPDATE_CHECK), now = Math.round(+new Date / 1000);
if (currentVersion === SCRIPT_VERSION && now - lastCheck < CHECK_INTERVAL_SECONDS)
return;
- setPref("version_last_check", now), fetch("https://api.github.com/repos/redphx/better-xcloud/releases/latest").then((response) => response.json()).then((json) => {
- setPref("version_latest", json.tag_name.substring(1)), setPref("version_current", SCRIPT_VERSION);
+ setPref(PrefKey.LAST_UPDATE_CHECK, now), fetch("https://api.github.com/repos/redphx/better-xcloud/releases/latest").then((response) => response.json()).then((json) => {
+ setPref(PrefKey.LATEST_VERSION, json.tag_name.substring(1)), setPref(PrefKey.CURRENT_VERSION, SCRIPT_VERSION);
}), Translations.updateTranslations(currentVersion === SCRIPT_VERSION);
}
function disablePwa() {
@@ -3132,9 +3407,9 @@ async function copyToClipboard(text, showToast = !0) {
class SoundShortcut {
static adjustGainNodeVolume(amount) {
- if (!getPref("audio_enable_volume_control"))
+ if (!getPref(PrefKey.AUDIO_ENABLE_VOLUME_CONTROL))
return 0;
- const currentValue = getPref("audio_volume");
+ const currentValue = getPref(PrefKey.AUDIO_VOLUME);
let nearestValue;
if (amount > 0)
nearestValue = ceilToNearest(currentValue, amount);
@@ -3145,17 +3420,17 @@ class SoundShortcut {
newValue = nearestValue;
else
newValue = currentValue + amount;
- return newValue = setPref("audio_volume", newValue, !0), SoundShortcut.setGainNodeVolume(newValue), Toast.show(`${t("stream")} ❯ ${t("volume")}`, newValue + "%", { instant: !0 }), newValue;
+ return newValue = setPref(PrefKey.AUDIO_VOLUME, newValue, !0), SoundShortcut.setGainNodeVolume(newValue), Toast.show(`${t("stream")} ❯ ${t("volume")}`, newValue + "%", { instant: !0 }), newValue;
}
static setGainNodeVolume(value) {
STATES.currentStream.audioGainNode && (STATES.currentStream.audioGainNode.gain.value = value / 100);
}
static muteUnmute() {
- if (getPref("audio_enable_volume_control") && STATES.currentStream.audioGainNode) {
- const gainValue = STATES.currentStream.audioGainNode.gain.value, settingValue = getPref("audio_volume");
+ if (getPref(PrefKey.AUDIO_ENABLE_VOLUME_CONTROL) && STATES.currentStream.audioGainNode) {
+ const gainValue = STATES.currentStream.audioGainNode.gain.value, settingValue = getPref(PrefKey.AUDIO_VOLUME);
let targetValue;
if (settingValue === 0)
- targetValue = 100, setPref("audio_volume", targetValue, !0);
+ targetValue = 100, setPref(PrefKey.AUDIO_VOLUME, targetValue, !0);
else if (gainValue === 0)
targetValue = settingValue;
else
@@ -3345,7 +3620,7 @@ class TouchController {
};
const $style = document.createElement("style");
document.documentElement.appendChild($style), TouchController.#$style = $style;
- const PREF_STYLE_STANDARD = getPref("stream_touch_controller_style_standard"), PREF_STYLE_CUSTOM = getPref("stream_touch_controller_style_custom");
+ const PREF_STYLE_STANDARD = getPref(PrefKey.STREAM_TOUCH_CONTROLLER_STYLE_STANDARD), PREF_STYLE_CUSTOM = getPref(PrefKey.STREAM_TOUCH_CONTROLLER_STYLE_CUSTOM);
window.addEventListener(BxEvent.DATA_CHANNEL_CREATED, (e) => {
const dataChannel = e.dataChannel;
if (!dataChannel || dataChannel.label !== "message")
@@ -3422,12 +3697,12 @@ class VibrationManager {
return !!window.navigator.vibrate;
}
static updateGlobalVars(stopVibration = !0) {
- if (window.BX_ENABLE_CONTROLLER_VIBRATION = VibrationManager.supportControllerVibration() ? getPref("controller_enable_vibration") : !1, window.BX_VIBRATION_INTENSITY = getPref("controller_vibration_intensity") / 100, !VibrationManager.supportDeviceVibration()) {
+ if (window.BX_ENABLE_CONTROLLER_VIBRATION = VibrationManager.supportControllerVibration() ? getPref(PrefKey.CONTROLLER_ENABLE_VIBRATION) : !1, window.BX_VIBRATION_INTENSITY = getPref(PrefKey.CONTROLLER_VIBRATION_INTENSITY) / 100, !VibrationManager.supportDeviceVibration()) {
window.BX_ENABLE_DEVICE_VIBRATION = !1;
return;
}
stopVibration && window.navigator.vibrate(0);
- const value = getPref("controller_device_vibration");
+ const value = getPref(PrefKey.CONTROLLER_DEVICE_VIBRATION);
let enabled;
if (value === "on")
enabled = !0;
@@ -3567,19 +3842,19 @@ class BxSelectElement {
}
}
-var controller_shortcuts_default = "if (window.BX_EXPOSED.disableGamepadPolling) {\n this.inputConfiguration.useIntervalWorkerThreadForInput && this.intervalWorker ? this.intervalWorker.scheduleTimer(50) : this.pollGamepadssetTimeoutTimerID = setTimeout(this.pollGamepads, 50);\n return;\n}\n\nconst currentGamepad = ${gamepadVar};\n\n// Share button on XS controller\nif (currentGamepad.buttons[17] && currentGamepad.buttons[17].pressed) {\n window.dispatchEvent(new Event(BxEvent.CAPTURE_SCREENSHOT));\n}\n\nconst btnHome = currentGamepad.buttons[16];\nif (btnHome) {\n if (!this.bxHomeStates) {\n this.bxHomeStates = {};\n }\n\n let intervalMs = 0;\n let hijack = false;\n\n if (btnHome.pressed) {\n hijack = true;\n intervalMs = 16;\n this.gamepadIsIdle.set(currentGamepad.index, false);\n\n if (this.bxHomeStates[currentGamepad.index]) {\n const lastTimestamp = this.bxHomeStates[currentGamepad.index].timestamp;\n\n if (currentGamepad.timestamp !== lastTimestamp) {\n this.bxHomeStates[currentGamepad.index].timestamp = currentGamepad.timestamp;\n\n const handled = window.BX_EXPOSED.handleControllerShortcut(currentGamepad);\n if (handled) {\n this.bxHomeStates[currentGamepad.index].shortcutPressed += 1;\n }\n }\n } else {\n // First time pressing > save current timestamp\n window.BX_EXPOSED.resetControllerShortcut(currentGamepad.index);\n this.bxHomeStates[currentGamepad.index] = {\n shortcutPressed: 0,\n timestamp: currentGamepad.timestamp,\n };\n }\n } else if (this.bxHomeStates[currentGamepad.index]) {\n hijack = true;\n const info = structuredClone(this.bxHomeStates[currentGamepad.index]);\n\n // Home button released\n this.bxHomeStates[currentGamepad.index] = null;\n\n if (info.shortcutPressed === 0) {\n const fakeGamepadMappings = [{\n GamepadIndex: currentGamepad.index,\n A: 0,\n B: 0,\n X: 0,\n Y: 0,\n LeftShoulder: 0,\n RightShoulder: 0,\n LeftTrigger: 0,\n RightTrigger: 0,\n View: 0,\n Menu: 0,\n LeftThumb: 0,\n RightThumb: 0,\n DPadUp: 0,\n DPadDown: 0,\n DPadLeft: 0,\n DPadRight: 0,\n Nexus: 1,\n LeftThumbXAxis: 0,\n LeftThumbYAxis: 0,\n RightThumbXAxis: 0,\n RightThumbYAxis: 0,\n PhysicalPhysicality: 0,\n VirtualPhysicality: 0,\n Dirty: true,\n Virtual: false,\n }];\n\n const isLongPress = (currentGamepad.timestamp - info.timestamp) >= 500;\n intervalMs = isLongPress ? 500 : 100;\n\n this.inputSink.onGamepadInput(performance.now() - intervalMs, fakeGamepadMappings);\n } else {\n intervalMs = 4;\n }\n }\n\n if (hijack && intervalMs) {\n // Listen to next button press\n this.inputConfiguration.useIntervalWorkerThreadForInput && this.intervalWorker ? this.intervalWorker.scheduleTimer(intervalMs) : this.pollGamepadssetTimeoutTimerID = setTimeout(this.pollGamepads, intervalMs);\n\n // Hijack this button\n return;\n }\n}\n";
+var controller_shortcuts_default = "if (window.BX_EXPOSED.disableGamepadPolling) {\r\n this.inputConfiguration.useIntervalWorkerThreadForInput && this.intervalWorker ? this.intervalWorker.scheduleTimer(50) : this.pollGamepadssetTimeoutTimerID = setTimeout(this.pollGamepads, 50);\r\n return;\r\n}\r\n\r\nconst currentGamepad = ${gamepadVar};\r\n\r\n// Share button on XS controller\r\nif (currentGamepad.buttons[17] && currentGamepad.buttons[17].pressed) {\r\n window.dispatchEvent(new Event(BxEvent.CAPTURE_SCREENSHOT));\r\n}\r\n\r\nconst btnHome = currentGamepad.buttons[16];\r\nif (btnHome) {\r\n if (!this.bxHomeStates) {\r\n this.bxHomeStates = {};\r\n }\r\n\r\n let intervalMs = 0;\r\n let hijack = false;\r\n\r\n if (btnHome.pressed) {\r\n hijack = true;\r\n intervalMs = 16;\r\n this.gamepadIsIdle.set(currentGamepad.index, false);\r\n\r\n if (this.bxHomeStates[currentGamepad.index]) {\r\n const lastTimestamp = this.bxHomeStates[currentGamepad.index].timestamp;\r\n\r\n if (currentGamepad.timestamp !== lastTimestamp) {\r\n this.bxHomeStates[currentGamepad.index].timestamp = currentGamepad.timestamp;\r\n\r\n const handled = window.BX_EXPOSED.handleControllerShortcut(currentGamepad);\r\n if (handled) {\r\n this.bxHomeStates[currentGamepad.index].shortcutPressed += 1;\r\n }\r\n }\r\n } else {\r\n // First time pressing > save current timestamp\r\n window.BX_EXPOSED.resetControllerShortcut(currentGamepad.index);\r\n this.bxHomeStates[currentGamepad.index] = {\r\n shortcutPressed: 0,\r\n timestamp: currentGamepad.timestamp,\r\n };\r\n }\r\n } else if (this.bxHomeStates[currentGamepad.index]) {\r\n hijack = true;\r\n const info = structuredClone(this.bxHomeStates[currentGamepad.index]);\r\n\r\n // Home button released\r\n this.bxHomeStates[currentGamepad.index] = null;\r\n\r\n if (info.shortcutPressed === 0) {\r\n const fakeGamepadMappings = [{\r\n GamepadIndex: currentGamepad.index,\r\n A: 0,\r\n B: 0,\r\n X: 0,\r\n Y: 0,\r\n LeftShoulder: 0,\r\n RightShoulder: 0,\r\n LeftTrigger: 0,\r\n RightTrigger: 0,\r\n View: 0,\r\n Menu: 0,\r\n LeftThumb: 0,\r\n RightThumb: 0,\r\n DPadUp: 0,\r\n DPadDown: 0,\r\n DPadLeft: 0,\r\n DPadRight: 0,\r\n Nexus: 1,\r\n LeftThumbXAxis: 0,\r\n LeftThumbYAxis: 0,\r\n RightThumbXAxis: 0,\r\n RightThumbYAxis: 0,\r\n PhysicalPhysicality: 0,\r\n VirtualPhysicality: 0,\r\n Dirty: true,\r\n Virtual: false,\r\n }];\r\n\r\n const isLongPress = (currentGamepad.timestamp - info.timestamp) >= 500;\r\n intervalMs = isLongPress ? 500 : 100;\r\n\r\n this.inputSink.onGamepadInput(performance.now() - intervalMs, fakeGamepadMappings);\r\n } else {\r\n intervalMs = 4;\r\n }\r\n }\r\n\r\n if (hijack && intervalMs) {\r\n // Listen to next button press\r\n this.inputConfiguration.useIntervalWorkerThreadForInput && this.intervalWorker ? this.intervalWorker.scheduleTimer(intervalMs) : this.pollGamepadssetTimeoutTimerID = setTimeout(this.pollGamepads, intervalMs);\r\n\r\n // Hijack this button\r\n return;\r\n }\r\n}\r\n";
-var expose_stream_session_default = "window.BX_EXPOSED.streamSession = this;\n\nconst orgSetMicrophoneState = this.setMicrophoneState.bind(this);\nthis.setMicrophoneState = state => {\n orgSetMicrophoneState(state);\n\n const evt = new Event(BxEvent.MICROPHONE_STATE_CHANGED);\n evt.microphoneState = state;\n\n window.dispatchEvent(evt);\n};\n\nwindow.dispatchEvent(new Event(BxEvent.STREAM_SESSION_READY));\n\n// Patch updateDimensions() to make native touch work correctly with WebGL2\nlet updateDimensionsStr = this.updateDimensions.toString();\n\nif (updateDimensionsStr.startsWith('function ')) {\n updateDimensionsStr = updateDimensionsStr.substring(9);\n}\n\n// if(r){\nconst renderTargetVar = updateDimensionsStr.match(/if\\((\\w+)\\){/)[1];\n\nupdateDimensionsStr = updateDimensionsStr.replaceAll(renderTargetVar + '.scroll', 'scroll');\n\nupdateDimensionsStr = updateDimensionsStr.replace(`if(${renderTargetVar}){`, `\nif (${renderTargetVar}) {\n const scrollWidth = ${renderTargetVar}.dataset.width ? parseInt(${renderTargetVar}.dataset.width) : ${renderTargetVar}.scrollWidth;\n const scrollHeight = ${renderTargetVar}.dataset.height ? parseInt(${renderTargetVar}.dataset.height) : ${renderTargetVar}.scrollHeight;\n`);\n\neval(`this.updateDimensions = function ${updateDimensionsStr}`);\n";
+var expose_stream_session_default = "window.BX_EXPOSED.streamSession = this;\r\n\r\nconst orgSetMicrophoneState = this.setMicrophoneState.bind(this);\r\nthis.setMicrophoneState = state => {\r\n orgSetMicrophoneState(state);\r\n\r\n const evt = new Event(BxEvent.MICROPHONE_STATE_CHANGED);\r\n evt.microphoneState = state;\r\n\r\n window.dispatchEvent(evt);\r\n};\r\n\r\nwindow.dispatchEvent(new Event(BxEvent.STREAM_SESSION_READY));\r\n\r\n// Patch updateDimensions() to make native touch work correctly with WebGL2\r\nlet updateDimensionsStr = this.updateDimensions.toString();\r\n\r\nif (updateDimensionsStr.startsWith('function ')) {\r\n updateDimensionsStr = updateDimensionsStr.substring(9);\r\n}\r\n\r\n// if(r){\r\nconst renderTargetVar = updateDimensionsStr.match(/if\\((\\w+)\\){/)[1];\r\n\r\nupdateDimensionsStr = updateDimensionsStr.replaceAll(renderTargetVar + '.scroll', 'scroll');\r\n\r\nupdateDimensionsStr = updateDimensionsStr.replace(`if(${renderTargetVar}){`, `\r\nif (${renderTargetVar}) {\r\n const scrollWidth = ${renderTargetVar}.dataset.width ? parseInt(${renderTargetVar}.dataset.width) : ${renderTargetVar}.scrollWidth;\r\n const scrollHeight = ${renderTargetVar}.dataset.height ? parseInt(${renderTargetVar}.dataset.height) : ${renderTargetVar}.scrollHeight;\r\n`);\r\n\r\neval(`this.updateDimensions = function ${updateDimensionsStr}`);\r\n";
-var local_co_op_enable_default = "let match;\nlet onGamepadChangedStr = this.onGamepadChanged.toString();\n\nif (onGamepadChangedStr.startsWith('function ')) {\n onGamepadChangedStr = onGamepadChangedStr.substring(9);\n}\n\nonGamepadChangedStr = onGamepadChangedStr.replaceAll('0', 'arguments[1]');\neval(`this.onGamepadChanged = function ${onGamepadChangedStr}`);\n\nlet onGamepadInputStr = this.onGamepadInput.toString();\n\nmatch = onGamepadInputStr.match(/(\\w+\\.GamepadIndex)/);\nif (match) {\n const gamepadIndexVar = match[0];\n onGamepadInputStr = onGamepadInputStr.replace('this.gamepadStates.get(', `this.gamepadStates.get(${gamepadIndexVar},`);\n eval(`this.onGamepadInput = function ${onGamepadInputStr}`);\n BxLogger.info('supportLocalCoOp', '✅ Successfully patched local co-op support');\n} else {\n BxLogger.error('supportLocalCoOp', '❌ Unable to patch local co-op support');\n}\n";
+var local_co_op_enable_default = "let match;\r\nlet onGamepadChangedStr = this.onGamepadChanged.toString();\r\n\r\nif (onGamepadChangedStr.startsWith('function ')) {\r\n onGamepadChangedStr = onGamepadChangedStr.substring(9);\r\n}\r\n\r\nonGamepadChangedStr = onGamepadChangedStr.replaceAll('0', 'arguments[1]');\r\neval(`this.onGamepadChanged = function ${onGamepadChangedStr}`);\r\n\r\nlet onGamepadInputStr = this.onGamepadInput.toString();\r\n\r\nmatch = onGamepadInputStr.match(/(\\w+\\.GamepadIndex)/);\r\nif (match) {\r\n const gamepadIndexVar = match[0];\r\n onGamepadInputStr = onGamepadInputStr.replace('this.gamepadStates.get(', `this.gamepadStates.get(${gamepadIndexVar},`);\r\n eval(`this.onGamepadInput = function ${onGamepadInputStr}`);\r\n BxLogger.info('supportLocalCoOp', '✅ Successfully patched local co-op support');\r\n} else {\r\n BxLogger.error('supportLocalCoOp', '❌ Unable to patch local co-op support');\r\n}\r\n";
-var set_currently_focused_interactable_default = "e && BxEvent.dispatch(window, BxEvent.NAVIGATION_FOCUS_CHANGED, {element: e});\n";
+var set_currently_focused_interactable_default = "e && BxEvent.dispatch(window, BxEvent.NAVIGATION_FOCUS_CHANGED, {element: e});\r\n";
-var remote_play_enable_default = "connectMode: window.BX_REMOTE_PLAY_CONFIG ? \"xhome-connect\" : \"cloud-connect\",\nremotePlayServerId: (window.BX_REMOTE_PLAY_CONFIG && window.BX_REMOTE_PLAY_CONFIG.serverId) || '',\n";
+var remote_play_enable_default = "connectMode: window.BX_REMOTE_PLAY_CONFIG ? \"xhome-connect\" : \"cloud-connect\",\r\nremotePlayServerId: (window.BX_REMOTE_PLAY_CONFIG && window.BX_REMOTE_PLAY_CONFIG.serverId) || '',\r\n";
-var remote_play_keep_alive_default = "const msg = JSON.parse(e);\nif (msg.reason === 'WarningForBeingIdle' && !window.location.pathname.includes('/launch/')) {\n try {\n this.sendKeepAlive();\n return;\n } catch (ex) { console.log(ex); }\n}\n";
+var remote_play_keep_alive_default = "const msg = JSON.parse(e);\r\nif (msg.reason === 'WarningForBeingIdle' && !window.location.pathname.includes('/launch/')) {\r\n try {\r\n this.sendKeepAlive();\r\n return;\r\n } catch (ex) { console.log(ex); }\r\n}\r\n";
-var vibration_adjust_default = "if (!window.BX_ENABLE_CONTROLLER_VIBRATION) {\n return void(0);\n}\n\nconst intensity = window.BX_VIBRATION_INTENSITY;\nif (intensity === 0) {\n return void(0);\n}\n\nif (intensity < 1) {\n e.leftMotorPercent *= intensity;\n e.rightMotorPercent *= intensity;\n e.leftTriggerMotorPercent *= intensity;\n e.rightTriggerMotorPercent *= intensity;\n}\n";
+var vibration_adjust_default = "if (!window.BX_ENABLE_CONTROLLER_VIBRATION) {\r\n return void(0);\r\n}\r\n\r\nconst intensity = window.BX_VIBRATION_INTENSITY;\r\nif (intensity === 0) {\r\n return void(0);\r\n}\r\n\r\nif (intensity < 1) {\r\n e.leftMotorPercent *= intensity;\r\n e.rightMotorPercent *= intensity;\r\n e.leftTriggerMotorPercent *= intensity;\r\n e.rightTriggerMotorPercent *= intensity;\r\n}\r\n";
var FeatureGates = {
PwaPrompt: !1,
@@ -3587,13 +3862,21 @@ var FeatureGates = {
EnableUpdateRequiredPage: !1,
ShowForcedUpdateScreen: !1
};
-if (getPref("ui_home_context_menu_disabled"))
+if (getPref(PrefKey.UI_HOME_CONTEXT_MENU_DISABLED))
FeatureGates.EnableHomeContextMenu = !1;
-if (getPref("block_social_features"))
+if (getPref(PrefKey.BLOCK_SOCIAL_FEATURES))
FeatureGates.EnableGuideChatTab = !1;
if (BX_FLAGS.FeatureGates)
FeatureGates = Object.assign(BX_FLAGS.FeatureGates, FeatureGates);
+var GamePassCloudGallery;
+(function(GamePassCloudGallery2) {
+ GamePassCloudGallery2["ALL"] = "29a81209-df6f-41fd-a528-2ae6b91f719c";
+ GamePassCloudGallery2["MOST_POPULAR"] = "e7590b22-e299-44db-ae22-25c61405454c";
+ GamePassCloudGallery2["NATIVE_MKB"] = "8fa264dd-124f-4af3-97e8-596fcdf4b486";
+ GamePassCloudGallery2["TOUCH"] = "9c86f07a-f3e8-45ad-82a0-a1f759597059";
+})(GamePassCloudGallery || (GamePassCloudGallery = {}));
+
var ENDING_CHUNKS_PATCH_NAME = "loadingEndingChunks", LOG_TAG3 = "Patcher", PATCHES = {
disableAiTrack(str) {
const index = str.indexOf(".track=function(");
@@ -3632,7 +3915,7 @@ var ENDING_CHUNKS_PATCH_NAME = "loadingEndingChunks", LOG_TAG3 = "Patcher", PATC
websiteLayout(str) {
if (!str.includes('?"tv":"default"'))
return !1;
- const layout = getPref("ui_layout") === "tv" ? "tv" : "default";
+ const layout = getPref(PrefKey.UI_LAYOUT) === "tv" ? "tv" : "default";
return str.replace('?"tv":"default"', `?"${layout}":"${layout}"`);
},
remotePlayDirectConnectUrl(str) {
@@ -3674,7 +3957,7 @@ if (!!window.BX_REMOTE_PLAY_CONFIG) {
if (nextIndex === -1)
return !1;
let codeBlock = str.substring(index, nextIndex);
- if (getPref("block_tracking"))
+ if (getPref(PrefKey.BLOCK_TRACKING))
codeBlock = codeBlock.replaceAll("this.inputPollingIntervalStats.addValue", "");
const match = codeBlock.match(/this\.gamepadTimestamps\.set\((\w+)\.index/);
if (match) {
@@ -3785,7 +4068,7 @@ if (window.BX_EXPOSED.stopTakRendering) {
if (!str.includes("const{TakRenderer:"))
return !1;
let remotePlayCode = "";
- if (getPref("stream_touch_controller") !== "off" && getPref("stream_touch_controller_auto_off"))
+ if (getPref(PrefKey.STREAM_TOUCH_CONTROLLER) !== "off" && getPref(PrefKey.STREAM_TOUCH_CONTROLLER_AUTO_OFF))
remotePlayCode = `
const gamepads = window.navigator.getGamepads();
let gamepadFound = false;
@@ -3827,7 +4110,7 @@ window.BX_EXPOSED.showStreamMenu = e.onShowStreamMenu;
// Restore the "..." button
e.guideUI = null;
`;
- if (getPref("stream_touch_controller") === "off")
+ if (getPref(PrefKey.STREAM_TOUCH_CONTROLLER) === "off")
newCode += "e.canShowTakHUD = false;";
return str = str.replace("let{onCollapse", newCode + "let{onCollapse"), str;
},
@@ -3886,7 +4169,7 @@ BxLogger.info('patchRemotePlayMkb', ${configsVar});
patchTouchControlDefaultOpacity(str) {
if (!str.includes("opacityMultiplier:1"))
return !1;
- const newCode = `opacityMultiplier: ${(getPref("stream_touch_controller_default_opacity") / 100).toFixed(1)}`;
+ const newCode = `opacityMultiplier: ${(getPref(PrefKey.STREAM_TOUCH_CONTROLLER_DEFAULT_OPACITY) / 100).toFixed(1)}`;
return str = str.replace("opacityMultiplier:1", newCode), str;
},
patchShowSensorControls(str) {
@@ -3900,7 +4183,7 @@ BxLogger.info('patchRemotePlayMkb', ${configsVar});
return !1;
const newCode = `;
${expose_stream_session_default}
-true,this._connectionType=`;
+true` + ",this._connectionType=";
return str = str.replace(",this._connectionType=", newCode), str;
},
skipFeedbackDialog(str) {
@@ -3981,9 +4264,9 @@ true,this._connectionType=`;
return !1;
if (index = str.indexOf("const[", index - 300), index < 0)
return !1;
- const PREF_HIDE_SECTIONS = getPref("ui_hide_sections"), siglIds = [], sections = {
- "native-mkb": "8fa264dd-124f-4af3-97e8-596fcdf4b486",
- "most-popular": "e7590b22-e299-44db-ae22-25c61405454c"
+ const PREF_HIDE_SECTIONS = getPref(PrefKey.UI_HIDE_SECTIONS), siglIds = [], sections = {
+ [UiSection.NATIVE_MKB]: GamePassCloudGallery.NATIVE_MKB,
+ [UiSection.MOST_POPULAR]: GamePassCloudGallery.MOST_POPULAR
};
PREF_HIDE_SECTIONS.forEach((section) => {
const galleryId = sections[section];
@@ -4037,7 +4320,7 @@ if (this.baseStorageKey in window.BX_EXPOSED.overrideSettings) {
return str = str.substring(0, index) + 'BxEvent.dispatch(window, BxEvent.XCLOUD_RENDERING_COMPONENT, {component: "product-details"});' + str.substring(index), str;
}
}, PATCH_ORDERS = [
- ...getPref("native_mkb_enabled") === "on" ? [
+ ...getPref(PrefKey.NATIVE_MKB_ENABLED) === "on" ? [
"enableNativeMkb",
"patchMouseAndKeyboardEnabled",
"disableNativeRequestPointerLock",
@@ -4053,22 +4336,22 @@ if (this.baseStorageKey in window.BX_EXPOSED.overrideSettings) {
"enableTvRoutes",
AppInterface && "detectProductDetailsPage",
"overrideStorageGetSettings",
- getPref("ui_game_card_show_wait_time") && "patchSetCurrentlyFocusedInteractable",
- getPref("ui_layout") !== "default" && "websiteLayout",
- getPref("local_co_op_enabled") && "supportLocalCoOp",
- getPref("game_fortnite_force_console") && "forceFortniteConsole",
- getPref("ui_hide_sections").includes("friends") && "ignorePlayWithFriendsSection",
- getPref("ui_hide_sections").includes("all-games") && "ignoreAllGamesSection",
- getPref("ui_hide_sections").includes("touch") && "ignorePlayWithTouchSection",
- (getPref("ui_hide_sections").includes("native-mkb") || getPref("ui_hide_sections").includes("most-popular")) && "ignoreSiglSections",
- ...getPref("block_tracking") ? [
+ getPref(PrefKey.UI_GAME_CARD_SHOW_WAIT_TIME) && "patchSetCurrentlyFocusedInteractable",
+ getPref(PrefKey.UI_LAYOUT) !== "default" && "websiteLayout",
+ getPref(PrefKey.LOCAL_CO_OP_ENABLED) && "supportLocalCoOp",
+ getPref(PrefKey.GAME_FORTNITE_FORCE_CONSOLE) && "forceFortniteConsole",
+ getPref(PrefKey.UI_HIDE_SECTIONS).includes(UiSection.FRIENDS) && "ignorePlayWithFriendsSection",
+ getPref(PrefKey.UI_HIDE_SECTIONS).includes(UiSection.ALL_GAMES) && "ignoreAllGamesSection",
+ getPref(PrefKey.UI_HIDE_SECTIONS).includes(UiSection.TOUCH) && "ignorePlayWithTouchSection",
+ (getPref(PrefKey.UI_HIDE_SECTIONS).includes(UiSection.NATIVE_MKB) || getPref(PrefKey.UI_HIDE_SECTIONS).includes(UiSection.MOST_POPULAR)) && "ignoreSiglSections",
+ ...getPref(PrefKey.BLOCK_TRACKING) ? [
"disableAiTrack",
"disableTelemetry",
"blockWebRtcStatsCollector",
"disableIndexDbLogging",
"disableTelemetryProvider"
] : [],
- ...getPref("xhome_enabled") ? [
+ ...getPref(PrefKey.REMOTE_PLAY_ENABLED) ? [
"remotePlayKeepAlive",
"remotePlayDirectConnectUrl",
"remotePlayDisableAchievementToast",
@@ -4084,20 +4367,20 @@ if (this.baseStorageKey in window.BX_EXPOSED.overrideSettings) {
"patchStreamHud",
"playVibration",
"alwaysShowStreamHud",
- getPref("audio_enable_volume_control") && !getPref("stream_combine_sources") && "patchAudioMediaStream",
- getPref("audio_enable_volume_control") && getPref("stream_combine_sources") && "patchCombinedAudioVideoMediaStream",
- getPref("stream_disable_feedback_dialog") && "skipFeedbackDialog",
+ getPref(PrefKey.AUDIO_ENABLE_VOLUME_CONTROL) && !getPref(PrefKey.STREAM_COMBINE_SOURCES) && "patchAudioMediaStream",
+ getPref(PrefKey.AUDIO_ENABLE_VOLUME_CONTROL) && getPref(PrefKey.STREAM_COMBINE_SOURCES) && "patchCombinedAudioVideoMediaStream",
+ getPref(PrefKey.STREAM_DISABLE_FEEDBACK_DIALOG) && "skipFeedbackDialog",
...STATES.userAgent.capabilities.touch ? [
- getPref("stream_touch_controller") === "all" && "patchShowSensorControls",
- getPref("stream_touch_controller") === "all" && "exposeTouchLayoutManager",
- (getPref("stream_touch_controller") === "off" || getPref("stream_touch_controller_auto_off") || !STATES.userAgent.capabilities.touch) && "disableTakRenderer",
- getPref("stream_touch_controller_default_opacity") !== 100 && "patchTouchControlDefaultOpacity",
+ getPref(PrefKey.STREAM_TOUCH_CONTROLLER) === "all" && "patchShowSensorControls",
+ getPref(PrefKey.STREAM_TOUCH_CONTROLLER) === "all" && "exposeTouchLayoutManager",
+ (getPref(PrefKey.STREAM_TOUCH_CONTROLLER) === "off" || getPref(PrefKey.STREAM_TOUCH_CONTROLLER_AUTO_OFF) || !STATES.userAgent.capabilities.touch) && "disableTakRenderer",
+ getPref(PrefKey.STREAM_TOUCH_CONTROLLER_DEFAULT_OPACITY) !== 100 && "patchTouchControlDefaultOpacity",
"patchBabylonRendererClass"
] : [],
BX_FLAGS.EnableXcloudLogging && "enableConsoleLogging",
"patchPollGamepads",
- getPref("stream_combine_sources") && "streamCombineSources",
- ...getPref("xhome_enabled") ? [
+ getPref(PrefKey.STREAM_COMBINE_SOURCES) && "streamCombineSources",
+ ...getPref(PrefKey.REMOTE_PLAY_ENABLED) ? [
"patchRemotePlayMkb",
"remotePlayConnectMode"
] : []
@@ -4239,7 +4522,7 @@ class SettingsNavigationDialog extends NavigationDialog {
helpUrl: "https://better-xcloud.github.io/features/",
items: [
($parent) => {
- const PREF_LATEST_VERSION = getPref("version_latest"), topButtons = [];
+ const PREF_LATEST_VERSION = getPref(PrefKey.LATEST_VERSION), topButtons = [];
if (!SCRIPT_VERSION.includes("beta") && PREF_LATEST_VERSION && PREF_LATEST_VERSION != SCRIPT_VERSION)
topButtons.push(createButton({
label: `🌟 Version ${PREF_LATEST_VERSION} available`,
@@ -4280,52 +4563,52 @@ class SettingsNavigationDialog extends NavigationDialog {
}, ...topButtons);
$parent.appendChild($div);
},
- "bx_locale",
- "server_bypass_restriction",
- "ui_controller_friendly",
- "xhome_enabled"
+ PrefKey.BETTER_XCLOUD_LOCALE,
+ PrefKey.SERVER_BYPASS_RESTRICTION,
+ PrefKey.UI_CONTROLLER_FRIENDLY,
+ PrefKey.REMOTE_PLAY_ENABLED
]
}, {
group: "server",
label: t("server"),
items: [
- "server_region",
- "stream_preferred_locale",
- "prefer_ipv6_server"
+ PrefKey.SERVER_REGION,
+ PrefKey.STREAM_PREFERRED_LOCALE,
+ PrefKey.PREFER_IPV6_SERVER
]
}, {
group: "stream",
label: t("stream"),
items: [
- "stream_target_resolution",
- "stream_codec_profile",
- "bitrate_video_max",
- "audio_enable_volume_control",
- "stream_disable_feedback_dialog",
- "screenshot_apply_filters",
- "audio_mic_on_playing",
- "game_fortnite_force_console",
- "stream_combine_sources"
+ PrefKey.STREAM_TARGET_RESOLUTION,
+ PrefKey.STREAM_CODEC_PROFILE,
+ PrefKey.BITRATE_VIDEO_MAX,
+ PrefKey.AUDIO_ENABLE_VOLUME_CONTROL,
+ PrefKey.STREAM_DISABLE_FEEDBACK_DIALOG,
+ PrefKey.SCREENSHOT_APPLY_FILTERS,
+ PrefKey.AUDIO_MIC_ON_PLAYING,
+ PrefKey.GAME_FORTNITE_FORCE_CONSOLE,
+ PrefKey.STREAM_COMBINE_SOURCES
]
}, {
group: "game-bar",
label: t("game-bar"),
items: [
- "game_bar_position"
+ PrefKey.GAME_BAR_POSITION
]
}, {
group: "co-op",
label: t("local-co-op"),
items: [
- "local_co_op_enabled"
+ PrefKey.LOCAL_CO_OP_ENABLED
]
}, {
group: "mkb",
label: t("mouse-and-keyboard"),
items: [
- "native_mkb_enabled",
- "mkb_enabled",
- "mkb_hide_idle_cursor"
+ PrefKey.NATIVE_MKB_ENABLED,
+ PrefKey.MKB_ENABLED,
+ PrefKey.MKB_HIDE_IDLE_CURSOR
]
}, {
group: "touch-control",
@@ -4333,48 +4616,48 @@ class SettingsNavigationDialog extends NavigationDialog {
note: !STATES.userAgent.capabilities.touch ? "⚠️ " + t("device-unsupported-touch") : null,
unsupported: !STATES.userAgent.capabilities.touch,
items: [
- "stream_touch_controller",
- "stream_touch_controller_auto_off",
- "stream_touch_controller_default_opacity",
- "stream_touch_controller_style_standard",
- "stream_touch_controller_style_custom"
+ PrefKey.STREAM_TOUCH_CONTROLLER,
+ PrefKey.STREAM_TOUCH_CONTROLLER_AUTO_OFF,
+ PrefKey.STREAM_TOUCH_CONTROLLER_DEFAULT_OPACITY,
+ PrefKey.STREAM_TOUCH_CONTROLLER_STYLE_STANDARD,
+ PrefKey.STREAM_TOUCH_CONTROLLER_STYLE_CUSTOM
]
}, {
group: "loading-screen",
label: t("loading-screen"),
items: [
- "ui_loading_screen_game_art",
- "ui_loading_screen_wait_time",
- "ui_loading_screen_rocket"
+ PrefKey.UI_LOADING_SCREEN_GAME_ART,
+ PrefKey.UI_LOADING_SCREEN_WAIT_TIME,
+ PrefKey.UI_LOADING_SCREEN_ROCKET
]
}, {
group: "ui",
label: t("ui"),
items: [
- "ui_layout",
- "ui_game_card_show_wait_time",
- "ui_home_context_menu_disabled",
- "controller_show_connection_status",
- "stream_simplify_menu",
- "skip_splash_video",
- !AppInterface && "ui_scrollbar_hide",
- "hide_dots_icon",
- "reduce_animations",
- "block_social_features",
- "ui_hide_sections"
+ PrefKey.UI_LAYOUT,
+ PrefKey.UI_GAME_CARD_SHOW_WAIT_TIME,
+ PrefKey.UI_HOME_CONTEXT_MENU_DISABLED,
+ PrefKey.CONTROLLER_SHOW_CONNECTION_STATUS,
+ PrefKey.STREAM_SIMPLIFY_MENU,
+ PrefKey.SKIP_SPLASH_VIDEO,
+ !AppInterface && PrefKey.UI_SCROLLBAR_HIDE,
+ PrefKey.HIDE_DOTS_ICON,
+ PrefKey.REDUCE_ANIMATIONS,
+ PrefKey.BLOCK_SOCIAL_FEATURES,
+ PrefKey.UI_HIDE_SECTIONS
]
}, {
group: "other",
label: t("other"),
items: [
- "block_tracking"
+ PrefKey.BLOCK_TRACKING
]
}, {
group: "advanced",
label: t("advanced"),
items: [
{
- pref: "user_agent_profile",
+ pref: PrefKey.USER_AGENT_PROFILE,
onCreated: (setting, $control) => {
const defaultUserAgent = window.navigator.orgUserAgent || window.navigator.userAgent, $inpCustomUserAgent = CE("input", {
id: `bx_setting_inp_${setting.pref}`,
@@ -4436,18 +4719,18 @@ class SettingsNavigationDialog extends NavigationDialog {
label: t("audio"),
helpUrl: "https://better-xcloud.github.io/ingame-features/#audio",
items: [{
- pref: "audio_volume",
+ pref: PrefKey.AUDIO_VOLUME,
onChange: (e, value) => {
SoundShortcut.setGainNodeVolume(value);
},
params: {
- disabled: !getPref("audio_enable_volume_control")
+ disabled: !getPref(PrefKey.AUDIO_ENABLE_VOLUME_CONTROL)
},
onCreated: (setting, $elm) => {
const $range = $elm.querySelector("input[type=range");
window.addEventListener(BxEvent.SETTINGS_CHANGED, (e) => {
const { storageKey, settingKey, settingValue } = e;
- if (storageKey !== "better_xcloud" || settingKey !== "audio_volume")
+ if (storageKey !== StorageKey.GLOBAL || settingKey !== PrefKey.AUDIO_VOLUME)
return;
$range.value = settingValue, BxEvent.dispatch($range, "input", {
ignoreOnChange: !0
@@ -4460,10 +4743,10 @@ class SettingsNavigationDialog extends NavigationDialog {
label: t("video"),
helpUrl: "https://better-xcloud.github.io/ingame-features/#video",
items: [{
- pref: "video_player_type",
+ pref: PrefKey.VIDEO_PLAYER_TYPE,
onChange: onChangeVideoPlayerType
}, {
- pref: "video_power_preference",
+ pref: PrefKey.VIDEO_POWER_PREFERENCE,
onChange: () => {
const streamPlayer = STATES.currentStream.streamPlayer;
if (!streamPlayer)
@@ -4471,22 +4754,22 @@ class SettingsNavigationDialog extends NavigationDialog {
streamPlayer.reloadPlayer(), updateVideoPlayer();
}
}, {
- pref: "video_processing",
+ pref: PrefKey.VIDEO_PROCESSING,
onChange: updateVideoPlayer
}, {
- pref: "video_ratio",
+ pref: PrefKey.VIDEO_RATIO,
onChange: updateVideoPlayer
}, {
- pref: "video_sharpness",
+ pref: PrefKey.VIDEO_SHARPNESS,
onChange: updateVideoPlayer
}, {
- pref: "video_saturation",
+ pref: PrefKey.VIDEO_SATURATION,
onChange: updateVideoPlayer
}, {
- pref: "video_contrast",
+ pref: PrefKey.VIDEO_CONTRAST,
onChange: updateVideoPlayer
}, {
- pref: "video_brightness",
+ pref: PrefKey.VIDEO_BRIGHTNESS,
onChange: updateVideoPlayer
}]
}];
@@ -4496,15 +4779,15 @@ class SettingsNavigationDialog extends NavigationDialog {
label: t("controller"),
helpUrl: "https://better-xcloud.github.io/ingame-features/#controller",
items: [{
- pref: "controller_enable_vibration",
+ pref: PrefKey.CONTROLLER_ENABLE_VIBRATION,
unsupported: !VibrationManager.supportControllerVibration(),
onChange: () => VibrationManager.updateGlobalVars()
}, {
- pref: "controller_device_vibration",
+ pref: PrefKey.CONTROLLER_DEVICE_VIBRATION,
unsupported: !VibrationManager.supportDeviceVibration(),
onChange: () => VibrationManager.updateGlobalVars()
}, (VibrationManager.supportControllerVibration() || VibrationManager.supportDeviceVibration()) && {
- pref: "controller_vibration_intensity",
+ pref: PrefKey.CONTROLLER_VIBRATION_INTENSITY,
unsupported: !VibrationManager.supportDeviceVibration(),
onChange: () => VibrationManager.updateGlobalVars()
}]
@@ -4555,12 +4838,12 @@ class SettingsNavigationDialog extends NavigationDialog {
group: "native-mkb",
label: t("native-mkb"),
items: [{
- pref: "native_mkb_scroll_y_sensitivity",
+ pref: PrefKey.NATIVE_MKB_SCROLL_VERTICAL_SENSITIVITY,
onChange: (e, value) => {
NativeMkbHandler.getInstance().setVerticalScrollMultiplier(value / 100);
}
}, {
- pref: "native_mkb_scroll_x_sensitivity",
+ pref: PrefKey.NATIVE_MKB_SCROLL_HORIZONTAL_SENSITIVITY,
onChange: (e, value) => {
NativeMkbHandler.getInstance().setHorizontalScrollMultiplier(value / 100);
}
@@ -4577,37 +4860,37 @@ class SettingsNavigationDialog extends NavigationDialog {
helpUrl: "https://better-xcloud.github.io/stream-stats/",
items: [
{
- pref: "stats_show_when_playing"
+ pref: PrefKey.STATS_SHOW_WHEN_PLAYING
},
{
- pref: "stats_quick_glance",
+ pref: PrefKey.STATS_QUICK_GLANCE,
onChange: (e) => {
const streamStats = StreamStats.getInstance();
e.target.checked ? streamStats.quickGlanceSetup() : streamStats.quickGlanceStop();
}
},
{
- pref: "stats_items",
+ pref: PrefKey.STATS_ITEMS,
onChange: StreamStats.refreshStyles
},
{
- pref: "stats_position",
+ pref: PrefKey.STATS_POSITION,
onChange: StreamStats.refreshStyles
},
{
- pref: "stats_text_size",
+ pref: PrefKey.STATS_TEXT_SIZE,
onChange: StreamStats.refreshStyles
},
{
- pref: "stats_opacity",
+ pref: PrefKey.STATS_OPACITY,
onChange: StreamStats.refreshStyles
},
{
- pref: "stats_transparent",
+ pref: PrefKey.STATS_TRANSPARENT,
onChange: StreamStats.refreshStyles
},
{
- pref: "stats_conditional_formatting",
+ pref: PrefKey.STATS_CONDITIONAL_FORMATTING,
onChange: StreamStats.refreshStyles
}
]
@@ -4628,12 +4911,12 @@ class SettingsNavigationDialog extends NavigationDialog {
group: "controller",
items: this.TAB_CONTROLLER_ITEMS
},
- getPref("mkb_enabled") && {
+ getPref(PrefKey.MKB_ENABLED) && {
icon: BxIcon.VIRTUAL_CONTROLLER,
group: "mkb",
items: this.TAB_VIRTUAL_CONTROLLER_ITEMS
},
- AppInterface && getPref("native_mkb_enabled") === "on" && {
+ AppInterface && getPref(PrefKey.NATIVE_MKB_ENABLED) === "on" && {
icon: BxIcon.NATIVE_MKB,
group: "native-mkb",
items: this.TAB_NATIVE_MKB_ITEMS
@@ -4664,7 +4947,7 @@ class SettingsNavigationDialog extends NavigationDialog {
return;
if (onChangeVideoPlayerType(), STATES.userAgent.capabilities.touch)
BxEvent.dispatch(window, BxEvent.CUSTOM_TOUCH_LAYOUTS_LOADED);
- const $selectUserAgent = document.querySelector(`#bx_setting_${"user_agent_profile"}`);
+ const $selectUserAgent = document.querySelector(`#bx_setting_${PrefKey.USER_AGENT_PROFILE}`);
if ($selectUserAgent)
$selectUserAgent.disabled = !0, BxEvent.dispatch($selectUserAgent, "input", {}), $selectUserAgent.disabled = !1;
}
@@ -4695,7 +4978,7 @@ class SettingsNavigationDialog extends NavigationDialog {
});
$control.name = $control.id, $control.addEventListener("input", (e) => {
setPref(setting.pref, e.target.value), this.onGlobalSettingChanged(e);
- }), selectedValue = getPref("server_region"), setting.options = {};
+ }), selectedValue = getPref(PrefKey.SERVER_REGION), setting.options = {};
for (let regionName in STATES.serverRegions) {
const region = STATES.serverRegions[regionName];
let value = regionName, label = `${region.shortName} - ${regionName}`;
@@ -4724,12 +5007,12 @@ class SettingsNavigationDialog extends NavigationDialog {
else
$control = setting.content;
else if (!setting.unsupported) {
- if (pref === "server_region")
+ if (pref === PrefKey.SERVER_REGION)
$control = this.renderServerSetting(setting);
- else if (pref === "bx_locale")
+ else if (pref === PrefKey.BETTER_XCLOUD_LOCALE)
$control = SettingElement.fromPref(pref, STORAGE.Global, async (e) => {
const newLocale = e.target.value;
- if (getPref("ui_controller_friendly")) {
+ if (getPref(PrefKey.UI_CONTROLLER_FRIENDLY)) {
let timeoutId = e.target.timeoutId;
timeoutId && window.clearTimeout(timeoutId), e.target.timeoutId = window.setTimeout(() => {
Translations.refreshLocale(newLocale), Translations.updateTranslations();
@@ -4738,10 +5021,10 @@ class SettingsNavigationDialog extends NavigationDialog {
Translations.refreshLocale(newLocale), Translations.updateTranslations();
this.onGlobalSettingChanged(e);
});
- else if (pref === "user_agent_profile")
- $control = SettingElement.fromPref("user_agent_profile", STORAGE.Global, (e) => {
+ else if (pref === PrefKey.USER_AGENT_PROFILE)
+ $control = SettingElement.fromPref(PrefKey.USER_AGENT_PROFILE, STORAGE.Global, (e) => {
const value = e.target.value;
- let isCustom = value === "custom", userAgent2 = UserAgent.get(value);
+ let isCustom = value === UserAgentProfile.CUSTOM, userAgent2 = UserAgent.get(value);
UserAgent.updateStorage(value);
const $inp = $control.nextElementSibling;
$inp.value = userAgent2, $inp.readOnly = !isCustom, $inp.disabled = !isCustom, !e.target.disabled && this.onGlobalSettingChanged(e);
@@ -4752,7 +5035,7 @@ class SettingsNavigationDialog extends NavigationDialog {
onChange = this.onGlobalSettingChanged.bind(this);
$control = SettingElement.fromPref(pref, STORAGE.Global, onChange, setting.params);
}
- if ($control instanceof HTMLSelectElement && getPref("ui_controller_friendly"))
+ if ($control instanceof HTMLSelectElement && getPref(PrefKey.UI_CONTROLLER_FRIENDLY))
$control = BxSelectElement.wrap($control);
}
let prefDefinition = null;
@@ -4794,8 +5077,8 @@ class SettingsNavigationDialog extends NavigationDialog {
return this.dialogManager.focus($tabs);
},
loop: (direction) => {
- if (direction === 1 || direction === 3)
- return this.focusVisibleTab(direction === 1 ? "last" : "first"), !0;
+ if (direction === NavigationDirection.UP || direction === NavigationDirection.DOWN)
+ return this.focusVisibleTab(direction === NavigationDirection.UP ? "last" : "first"), !0;
return !1;
}
}
@@ -4822,8 +5105,8 @@ class SettingsNavigationDialog extends NavigationDialog {
orientation: "vertical",
focus: () => this.jumpToSettingGroup("next"),
loop: (direction) => {
- if (direction === 1 || direction === 3)
- return this.focusVisibleSetting(direction === 1 ? "last" : "first"), !0;
+ if (direction === NavigationDirection.UP || direction === NavigationDirection.DOWN)
+ return this.focusVisibleSetting(direction === NavigationDirection.UP ? "last" : "first"), !0;
return !1;
}
}
@@ -4958,7 +5241,7 @@ class SettingsNavigationDialog extends NavigationDialog {
}
let $target;
if ($header)
- $target = this.dialogManager.findNextTarget($header, 3, !1);
+ $target = this.dialogManager.findNextTarget($header, NavigationDirection.DOWN, !1);
if ($target)
return this.dialogManager.focus($target);
return !1;
@@ -4990,14 +5273,14 @@ class SettingsNavigationDialog extends NavigationDialog {
handleGamepad(button) {
let handled = !0;
switch (button) {
- case 4:
- case 5:
+ case GamepadKey.LB:
+ case GamepadKey.RB:
this.focusActiveTab();
break;
- case 6:
+ case GamepadKey.LT:
this.jumpToSettingGroup("previous");
break;
- case 7:
+ case GamepadKey.RT:
this.jumpToSettingGroup("next");
break;
default:
@@ -5015,6 +5298,9 @@ var LOG_TAG4 = "MkbHandler", PointerToMouseButton = {
};
class WebSocketMouseDataProvider extends MouseDataProvider {
+ constructor() {
+ super(...arguments);
+ }
#pointerClient;
#connected = !1;
init() {
@@ -5037,6 +5323,9 @@ class WebSocketMouseDataProvider extends MouseDataProvider {
}
class PointerLockMouseDataProvider extends MouseDataProvider {
+ constructor() {
+ super(...arguments);
+ }
init() {
}
start() {
@@ -5114,14 +5403,14 @@ class EmulatedMkbHandler extends MkbHandler {
constructor() {
super();
this.#STICK_MAP = {
- 102: [this.#LEFT_STICK_X, 0, -1],
- 103: [this.#LEFT_STICK_X, 0, 1],
- 100: [this.#LEFT_STICK_Y, 1, -1],
- 101: [this.#LEFT_STICK_Y, 1, 1],
- 202: [this.#RIGHT_STICK_X, 2, -1],
- 203: [this.#RIGHT_STICK_X, 2, 1],
- 200: [this.#RIGHT_STICK_Y, 3, -1],
- 201: [this.#RIGHT_STICK_Y, 3, 1]
+ [GamepadKey.LS_LEFT]: [this.#LEFT_STICK_X, 0, -1],
+ [GamepadKey.LS_RIGHT]: [this.#LEFT_STICK_X, 0, 1],
+ [GamepadKey.LS_UP]: [this.#LEFT_STICK_Y, 1, -1],
+ [GamepadKey.LS_DOWN]: [this.#LEFT_STICK_Y, 1, 1],
+ [GamepadKey.RS_LEFT]: [this.#RIGHT_STICK_X, 2, -1],
+ [GamepadKey.RS_RIGHT]: [this.#RIGHT_STICK_X, 2, 1],
+ [GamepadKey.RS_UP]: [this.#RIGHT_STICK_Y, 3, -1],
+ [GamepadKey.RS_DOWN]: [this.#RIGHT_STICK_Y, 3, 1]
};
}
isEnabled = () => this.#enabled;
@@ -5189,7 +5478,7 @@ class EmulatedMkbHandler extends MkbHandler {
};
#onMouseStopped = () => {
this.#detectMouseStoppedTimeout = null;
- const analog = this.#CURRENT_PRESET_DATA.mouse["map_to"] === 1 ? 0 : 1;
+ const analog = this.#CURRENT_PRESET_DATA.mouse[MkbPresetKey.MOUSE_MAP_TO] === MouseMapTo.LS ? GamepadStick.LEFT : GamepadStick.RIGHT;
this.#updateStick(analog, 0, 0);
};
handleMouseClick = (data) => {
@@ -5210,29 +5499,29 @@ class EmulatedMkbHandler extends MkbHandler {
this.#pressButton(buttonIndex, data.pressed);
};
handleMouseMove = (data) => {
- const mouseMapTo = this.#CURRENT_PRESET_DATA.mouse["map_to"];
- if (mouseMapTo === 0)
+ const mouseMapTo = this.#CURRENT_PRESET_DATA.mouse[MkbPresetKey.MOUSE_MAP_TO];
+ if (mouseMapTo === MouseMapTo.OFF)
return;
this.#detectMouseStoppedTimeout && clearTimeout(this.#detectMouseStoppedTimeout), this.#detectMouseStoppedTimeout = window.setTimeout(this.#onMouseStopped.bind(this), 50);
- const deadzoneCounterweight = this.#CURRENT_PRESET_DATA.mouse["deadzone_counterweight"];
- let x = data.movementX * this.#CURRENT_PRESET_DATA.mouse["sensitivity_x"], y = data.movementY * this.#CURRENT_PRESET_DATA.mouse["sensitivity_y"], length = this.#vectorLength(x, y);
+ const deadzoneCounterweight = this.#CURRENT_PRESET_DATA.mouse[MkbPresetKey.MOUSE_DEADZONE_COUNTERWEIGHT];
+ let x = data.movementX * this.#CURRENT_PRESET_DATA.mouse[MkbPresetKey.MOUSE_SENSITIVITY_X], y = data.movementY * this.#CURRENT_PRESET_DATA.mouse[MkbPresetKey.MOUSE_SENSITIVITY_Y], length = this.#vectorLength(x, y);
if (length !== 0 && length < deadzoneCounterweight)
x *= deadzoneCounterweight / length, y *= deadzoneCounterweight / length;
else if (length > EmulatedMkbHandler.MAXIMUM_STICK_RANGE)
x *= EmulatedMkbHandler.MAXIMUM_STICK_RANGE / length, y *= EmulatedMkbHandler.MAXIMUM_STICK_RANGE / length;
- const analog = mouseMapTo === 1 ? 0 : 1;
+ const analog = mouseMapTo === MouseMapTo.LS ? GamepadStick.LEFT : GamepadStick.RIGHT;
this.#updateStick(analog, x, y);
};
handleMouseWheel = (data) => {
let code = "";
if (data.vertical < 0)
- code = "ScrollUp";
+ code = WheelCode.SCROLL_UP;
else if (data.vertical > 0)
- code = "ScrollDown";
+ code = WheelCode.SCROLL_DOWN;
else if (data.horizontal < 0)
- code = "ScrollLeft";
+ code = WheelCode.SCROLL_LEFT;
else if (data.horizontal > 0)
- code = "ScrollRight";
+ code = WheelCode.SCROLL_RIGHT;
if (!code)
return !1;
const key = {
@@ -5259,7 +5548,7 @@ class EmulatedMkbHandler extends MkbHandler {
};
#getCurrentPreset = () => {
return new Promise((resolve) => {
- const presetId = getPref("mkb_default_preset_id");
+ const presetId = getPref(PrefKey.MKB_DEFAULT_PRESET_ID);
LocalDb.INSTANCE.getPreset(presetId).then((preset) => {
resolve(preset);
});
@@ -5376,19 +5665,28 @@ class EmulatedMkbHandler extends MkbHandler {
static setupEvents() {
window.addEventListener(BxEvent.STREAM_PLAYING, () => {
if (STATES.currentStream.titleInfo?.details.hasMkbSupport) {
- if (AppInterface && getPref("native_mkb_enabled") === "on")
+ if (AppInterface && getPref(PrefKey.NATIVE_MKB_ENABLED) === "on")
AppInterface && NativeMkbHandler.getInstance().init();
- } else if (getPref("mkb_enabled") && (AppInterface || !UserAgent.isMobile()))
+ } else if (getPref(PrefKey.MKB_ENABLED) && (AppInterface || !UserAgent.isMobile()))
BxLogger.info(LOG_TAG4, "Emulate MKB"), EmulatedMkbHandler.getInstance().init();
});
}
}
+var MicrophoneState;
+(function(MicrophoneState2) {
+ MicrophoneState2["REQUESTED"] = "Requested";
+ MicrophoneState2["ENABLED"] = "Enabled";
+ MicrophoneState2["MUTED"] = "Muted";
+ MicrophoneState2["NOT_ALLOWED"] = "NotAllowed";
+ MicrophoneState2["NOT_FOUND"] = "NotFound";
+})(MicrophoneState || (MicrophoneState = {}));
+
class MicrophoneShortcut {
static toggle(showToast = !0) {
if (!window.BX_EXPOSED.streamSession)
return !1;
- const enableMic = window.BX_EXPOSED.streamSession._microphoneState === "Enabled" ? !1 : !0;
+ const enableMic = window.BX_EXPOSED.streamSession._microphoneState === MicrophoneState.ENABLED ? !1 : !0;
try {
return window.BX_EXPOSED.streamSession.tryEnableChatAsync(enableMic), showToast && Toast.show(t("microphone"), t(enableMic ? "unmuted" : "muted"), { instant: !0 }), enableMic;
} catch (e) {
@@ -5404,6 +5702,23 @@ class StreamUiShortcut {
}
}
+var ShortcutAction;
+(function(ShortcutAction2) {
+ ShortcutAction2["BETTER_XCLOUD_SETTINGS_SHOW"] = "bx-settings-show";
+ ShortcutAction2["STREAM_SCREENSHOT_CAPTURE"] = "stream-screenshot-capture";
+ ShortcutAction2["STREAM_MENU_SHOW"] = "stream-menu-show";
+ ShortcutAction2["STREAM_STATS_TOGGLE"] = "stream-stats-toggle";
+ ShortcutAction2["STREAM_SOUND_TOGGLE"] = "stream-sound-toggle";
+ ShortcutAction2["STREAM_MICROPHONE_TOGGLE"] = "stream-microphone-toggle";
+ ShortcutAction2["STREAM_VOLUME_INC"] = "stream-volume-inc";
+ ShortcutAction2["STREAM_VOLUME_DEC"] = "stream-volume-dec";
+ ShortcutAction2["DEVICE_SOUND_TOGGLE"] = "device-sound-toggle";
+ ShortcutAction2["DEVICE_VOLUME_INC"] = "device-volume-inc";
+ ShortcutAction2["DEVICE_VOLUME_DEC"] = "device-volume-dec";
+ ShortcutAction2["DEVICE_BRIGHTNESS_INC"] = "device-brightness-inc";
+ ShortcutAction2["DEVICE_BRIGHTNESS_DEC"] = "device-brightness-dec";
+})(ShortcutAction || (ShortcutAction = {}));
+
class ControllerShortcut {
static #STORAGE_KEY = "better_xcloud_controller_shortcuts";
static #buttonsCache = {};
@@ -5425,7 +5740,7 @@ class ControllerShortcut {
const pressed = [];
let otherButtonPressed = !1;
return gamepad.buttons.forEach((button, index) => {
- if (button.pressed && index !== 16) {
+ if (button.pressed && index !== GamepadKey.HOME) {
if (otherButtonPressed = !0, pressed[index] = !0, actions[index] && !ControllerShortcut.#buttonsCache[gamepadIndex][index])
setTimeout(() => ControllerShortcut.#runAction(actions[index]), 0);
}
@@ -5433,35 +5748,35 @@ class ControllerShortcut {
}
static #runAction(action) {
switch (action) {
- case "bx-settings-show":
+ case ShortcutAction.BETTER_XCLOUD_SETTINGS_SHOW:
SettingsNavigationDialog.getInstance().show();
break;
- case "stream-screenshot-capture":
+ case ShortcutAction.STREAM_SCREENSHOT_CAPTURE:
Screenshot.takeScreenshot();
break;
- case "stream-stats-toggle":
+ case ShortcutAction.STREAM_STATS_TOGGLE:
StreamStats.getInstance().toggle();
break;
- case "stream-microphone-toggle":
+ case ShortcutAction.STREAM_MICROPHONE_TOGGLE:
MicrophoneShortcut.toggle();
break;
- case "stream-menu-show":
+ case ShortcutAction.STREAM_MENU_SHOW:
StreamUiShortcut.showHideStreamMenu();
break;
- case "stream-sound-toggle":
+ case ShortcutAction.STREAM_SOUND_TOGGLE:
SoundShortcut.muteUnmute();
break;
- case "stream-volume-inc":
+ case ShortcutAction.STREAM_VOLUME_INC:
SoundShortcut.adjustGainNodeVolume(10);
break;
- case "stream-volume-dec":
+ case ShortcutAction.STREAM_VOLUME_DEC:
SoundShortcut.adjustGainNodeVolume(-10);
break;
- case "device-brightness-inc":
- case "device-brightness-dec":
- case "device-sound-toggle":
- case "device-volume-inc":
- case "device-volume-dec":
+ case ShortcutAction.DEVICE_BRIGHTNESS_INC:
+ case ShortcutAction.DEVICE_BRIGHTNESS_DEC:
+ case ShortcutAction.DEVICE_SOUND_TOGGLE:
+ case ShortcutAction.DEVICE_VOLUME_INC:
+ case ShortcutAction.DEVICE_VOLUME_DEC:
AppInterface && AppInterface.runShortcut && AppInterface.runShortcut(action);
break;
}
@@ -5520,29 +5835,29 @@ class ControllerShortcut {
return JSON.parse(window.localStorage.getItem(ControllerShortcut.#STORAGE_KEY) || "{}");
}
static renderSettings() {
- const PREF_CONTROLLER_FRIENDLY_UI = getPref("ui_controller_friendly");
+ const PREF_CONTROLLER_FRIENDLY_UI = getPref(PrefKey.UI_CONTROLLER_FRIENDLY);
ControllerShortcut.#ACTIONS = ControllerShortcut.#getActionsFromStorage();
const buttons = new Map;
- buttons.set(3, "⇑"), buttons.set(0, "⇓"), buttons.set(1, "⇒"), buttons.set(2, "⇐"), buttons.set(12, "≻"), buttons.set(13, "≽"), buttons.set(14, "≺"), buttons.set(15, "≼"), buttons.set(8, "⇺"), buttons.set(9, "⇻"), buttons.set(4, "↘"), buttons.set(5, "↙"), buttons.set(6, "↖"), buttons.set(7, "↗"), buttons.set(10, "↺"), buttons.set(11, "↻");
+ buttons.set(GamepadKey.Y, PrompFont.Y), buttons.set(GamepadKey.A, PrompFont.A), buttons.set(GamepadKey.B, PrompFont.B), buttons.set(GamepadKey.X, PrompFont.X), buttons.set(GamepadKey.UP, PrompFont.UP), buttons.set(GamepadKey.DOWN, PrompFont.DOWN), buttons.set(GamepadKey.LEFT, PrompFont.LEFT), buttons.set(GamepadKey.RIGHT, PrompFont.RIGHT), buttons.set(GamepadKey.SELECT, PrompFont.SELECT), buttons.set(GamepadKey.START, PrompFont.START), buttons.set(GamepadKey.LB, PrompFont.LB), buttons.set(GamepadKey.RB, PrompFont.RB), buttons.set(GamepadKey.LT, PrompFont.LT), buttons.set(GamepadKey.RT, PrompFont.RT), buttons.set(GamepadKey.L3, PrompFont.L3), buttons.set(GamepadKey.R3, PrompFont.R3);
const actions = {
[t("better-xcloud")]: {
- ["bx-settings-show"]: [t("settings"), t("show")]
+ [ShortcutAction.BETTER_XCLOUD_SETTINGS_SHOW]: [t("settings"), t("show")]
},
[t("device")]: AppInterface && {
- ["device-sound-toggle"]: [t("sound"), t("toggle")],
- ["device-volume-inc"]: [t("volume"), t("increase")],
- ["device-volume-dec"]: [t("volume"), t("decrease")],
- ["device-brightness-inc"]: [t("brightness"), t("increase")],
- ["device-brightness-dec"]: [t("brightness"), t("decrease")]
+ [ShortcutAction.DEVICE_SOUND_TOGGLE]: [t("sound"), t("toggle")],
+ [ShortcutAction.DEVICE_VOLUME_INC]: [t("volume"), t("increase")],
+ [ShortcutAction.DEVICE_VOLUME_DEC]: [t("volume"), t("decrease")],
+ [ShortcutAction.DEVICE_BRIGHTNESS_INC]: [t("brightness"), t("increase")],
+ [ShortcutAction.DEVICE_BRIGHTNESS_DEC]: [t("brightness"), t("decrease")]
},
[t("stream")]: {
- ["stream-screenshot-capture"]: t("take-screenshot"),
- ["stream-sound-toggle"]: [t("sound"), t("toggle")],
- ["stream-volume-inc"]: getPref("audio_enable_volume_control") && [t("volume"), t("increase")],
- ["stream-volume-dec"]: getPref("audio_enable_volume_control") && [t("volume"), t("decrease")],
- ["stream-menu-show"]: [t("menu"), t("show")],
- ["stream-stats-toggle"]: [t("stats"), t("show-hide")],
- ["stream-microphone-toggle"]: [t("microphone"), t("toggle")]
+ [ShortcutAction.STREAM_SCREENSHOT_CAPTURE]: t("take-screenshot"),
+ [ShortcutAction.STREAM_SOUND_TOGGLE]: [t("sound"), t("toggle")],
+ [ShortcutAction.STREAM_VOLUME_INC]: getPref(PrefKey.AUDIO_ENABLE_VOLUME_CONTROL) && [t("volume"), t("increase")],
+ [ShortcutAction.STREAM_VOLUME_DEC]: getPref(PrefKey.AUDIO_ENABLE_VOLUME_CONTROL) && [t("volume"), t("decrease")],
+ [ShortcutAction.STREAM_MENU_SHOW]: [t("menu"), t("show")],
+ [ShortcutAction.STREAM_STATS_TOGGLE]: [t("stats"), t("show-hide")],
+ [ShortcutAction.STREAM_MICROPHONE_TOGGLE]: [t("microphone"), t("toggle")]
}
}, $baseSelect = CE("select", { autocomplete: "off" }, CE("option", { value: "" }, "---"));
for (let groupLabel in actions) {
@@ -5571,7 +5886,7 @@ class ControllerShortcut {
_nearby: {
focus: $profile
}
- }, $profile), CE("p", { class: "bx-shortcut-note" }, CE("span", { class: "bx-prompt" }, ""), ": " + t("controller-shortcuts-xbox-note"))));
+ }, $profile), CE("p", { class: "bx-shortcut-note" }, CE("span", { class: "bx-prompt" }, PrompFont.HOME), ": " + t("controller-shortcuts-xbox-note"))));
$selectProfile.addEventListener("input", (e) => {
ControllerShortcut.#switchProfile($selectProfile.value);
});
@@ -5591,7 +5906,7 @@ class ControllerShortcut {
for (let [button, prompt2] of buttons) {
const $row = CE("div", {
class: "bx-shortcut-row"
- }), $label = CE("label", { class: "bx-prompt" }, `${""} + ${prompt2}`), $div = CE("div", { class: "bx-shortcut-actions" });
+ }), $label = CE("label", { class: "bx-prompt" }, `${PrompFont.HOME} + ${prompt2}`), $div = CE("div", { class: "bx-shortcut-actions" });
if (!PREF_CONTROLLER_FRIENDLY_UI) {
const $fakeSelect = CE("select", { autocomplete: "off" }, CE("option", {}, "---"));
$div.appendChild($fakeSelect);
@@ -5612,18 +5927,27 @@ class ControllerShortcut {
}
}
+var SupportedInputType;
+(function(SupportedInputType2) {
+ SupportedInputType2["CONTROLLER"] = "Controller";
+ SupportedInputType2["MKB"] = "MKB";
+ SupportedInputType2["CUSTOM_TOUCH_OVERLAY"] = "CustomTouchOverlay";
+ SupportedInputType2["GENERIC_TOUCH"] = "GenericTouch";
+ SupportedInputType2["NATIVE_TOUCH"] = "NativeTouch";
+ SupportedInputType2["BATIVE_SENSOR"] = "NativeSensor";
+})(SupportedInputType || (SupportedInputType = {}));
var BxExposed = {
getTitleInfo: () => STATES.currentStream.titleInfo,
modifyTitleInfo: (titleInfo) => {
titleInfo = deepClone(titleInfo);
let supportedInputTypes = titleInfo.details.supportedInputTypes;
if (BX_FLAGS.ForceNativeMkbTitles?.includes(titleInfo.details.productId))
- supportedInputTypes.push("MKB");
- if (getPref("native_mkb_enabled") === "off")
- supportedInputTypes = supportedInputTypes.filter((i) => i !== "MKB");
- if (titleInfo.details.hasMkbSupport = supportedInputTypes.includes("MKB"), STATES.userAgent.capabilities.touch) {
- let touchControllerAvailability = getPref("stream_touch_controller");
- if (touchControllerAvailability !== "off" && getPref("stream_touch_controller_auto_off")) {
+ supportedInputTypes.push(SupportedInputType.MKB);
+ if (getPref(PrefKey.NATIVE_MKB_ENABLED) === "off")
+ supportedInputTypes = supportedInputTypes.filter((i) => i !== SupportedInputType.MKB);
+ if (titleInfo.details.hasMkbSupport = supportedInputTypes.includes(SupportedInputType.MKB), STATES.userAgent.capabilities.touch) {
+ let touchControllerAvailability = getPref(PrefKey.STREAM_TOUCH_CONTROLLER);
+ if (touchControllerAvailability !== "off" && getPref(PrefKey.STREAM_TOUCH_CONTROLLER_AUTO_OFF)) {
const gamepads = window.navigator.getGamepads();
let gamepadFound = !1;
for (let gamepad of gamepads)
@@ -5634,9 +5958,9 @@ var BxExposed = {
gamepadFound && (touchControllerAvailability = "off");
}
if (touchControllerAvailability === "off")
- supportedInputTypes = supportedInputTypes.filter((i) => i !== "CustomTouchOverlay" && i !== "GenericTouch"), titleInfo.details.supportedTabs = [];
- if (titleInfo.details.hasNativeTouchSupport = supportedInputTypes.includes("NativeTouch"), titleInfo.details.hasTouchSupport = titleInfo.details.hasNativeTouchSupport || supportedInputTypes.includes("CustomTouchOverlay") || supportedInputTypes.includes("GenericTouch"), !titleInfo.details.hasTouchSupport && touchControllerAvailability === "all")
- titleInfo.details.hasFakeTouchSupport = !0, supportedInputTypes.push("GenericTouch");
+ supportedInputTypes = supportedInputTypes.filter((i) => i !== SupportedInputType.CUSTOM_TOUCH_OVERLAY && i !== SupportedInputType.GENERIC_TOUCH), titleInfo.details.supportedTabs = [];
+ if (titleInfo.details.hasNativeTouchSupport = supportedInputTypes.includes(SupportedInputType.NATIVE_TOUCH), titleInfo.details.hasTouchSupport = titleInfo.details.hasNativeTouchSupport || supportedInputTypes.includes(SupportedInputType.CUSTOM_TOUCH_OVERLAY) || supportedInputTypes.includes(SupportedInputType.GENERIC_TOUCH), !titleInfo.details.hasTouchSupport && touchControllerAvailability === "all")
+ titleInfo.details.hasFakeTouchSupport = !0, supportedInputTypes.push(SupportedInputType.GENERIC_TOUCH);
}
return titleInfo.details.supportedInputTypes = supportedInputTypes, STATES.currentStream.titleInfo = titleInfo, BxEvent.dispatch(window, BxEvent.TITLE_INFO_READY), titleInfo;
},
@@ -5689,7 +6013,7 @@ function localRedirect(path) {
window.localRedirect = localRedirect;
function getPreferredServerRegion(shortName = !1) {
- let preferredRegion = getPref("server_region");
+ let preferredRegion = getPref(PrefKey.SERVER_REGION);
if (preferredRegion in STATES.serverRegions)
if (shortName && STATES.serverRegions[preferredRegion].shortName)
return STATES.serverRegions[preferredRegion].shortName;
@@ -5725,13 +6049,13 @@ class HeaderSection {
SettingsNavigationDialog.getInstance().show();
}
});
- static #$buttonsWrapper = CE("div", {}, getPref("xhome_enabled") ? HeaderSection.#$remotePlayBtn : null, HeaderSection.#$settingsBtn);
+ static #$buttonsWrapper = CE("div", {}, getPref(PrefKey.REMOTE_PLAY_ENABLED) ? HeaderSection.#$remotePlayBtn : null, HeaderSection.#$settingsBtn);
static #observer;
static #timeout;
static #injectSettingsButton($parent) {
if (!$parent)
return;
- const PREF_LATEST_VERSION = getPref("version_latest"), $settingsBtn = HeaderSection.#$settingsBtn;
+ const PREF_LATEST_VERSION = getPref(PrefKey.LATEST_VERSION), $settingsBtn = HeaderSection.#$settingsBtn;
if ($settingsBtn.querySelector("span").textContent = getPreferredServerRegion(!0) || t("better-xcloud"), !SCRIPT_VERSION.includes("beta") && PREF_LATEST_VERSION && PREF_LATEST_VERSION !== SCRIPT_VERSION)
$settingsBtn.setAttribute("data-update-available", "true");
$parent.appendChild(HeaderSection.#$buttonsWrapper);
@@ -5755,17 +6079,24 @@ class HeaderSection {
}
}
-var LOG_TAG5 = "RemotePlay";
+var LOG_TAG5 = "RemotePlay", RemotePlayConsoleState;
+(function(RemotePlayConsoleState2) {
+ RemotePlayConsoleState2["ON"] = "On";
+ RemotePlayConsoleState2["OFF"] = "Off";
+ RemotePlayConsoleState2["STANDBY"] = "ConnectedStandby";
+ RemotePlayConsoleState2["UNKNOWN"] = "Unknown";
+})(RemotePlayConsoleState || (RemotePlayConsoleState = {}));
+
class RemotePlay {
static XCLOUD_TOKEN;
static XHOME_TOKEN;
static #CONSOLES;
static #REGIONS;
static #STATE_LABELS = {
- ["On"]: t("powered-on"),
- ["Off"]: t("powered-off"),
- ["ConnectedStandby"]: t("standby"),
- ["Unknown"]: t("unknown")
+ [RemotePlayConsoleState.ON]: t("powered-on"),
+ [RemotePlayConsoleState.OFF]: t("powered-off"),
+ [RemotePlayConsoleState.STANDBY]: t("standby"),
+ [RemotePlayConsoleState.UNKNOWN]: t("unknown")
};
static BASE_DEVICE_INFO = {
appInfo: {
@@ -5823,7 +6154,7 @@ class RemotePlay {
$fragment.appendChild(CE("span", {}, t("no-consoles-found"))), RemotePlay.#$content = CE("div", {}, $fragment);
return;
}
- const $settingNote = CE("p", {}), resolutions = [1080, 720], currentResolution = getPref("xhome_resolution"), $resolutionGroup = CE("div", {});
+ const $settingNote = CE("p", {}), resolutions = [1080, 720], currentResolution = getPref(PrefKey.REMOTE_PLAY_RESOLUTION), $resolutionGroup = CE("div", {});
for (let resolution of resolutions) {
const value = `${resolution}p`, id2 = `bx_radio_xhome_resolution_${resolution}`, $radio = CE("input", {
type: "radio",
@@ -5833,7 +6164,7 @@ class RemotePlay {
}, value);
$radio.addEventListener("change", (e) => {
const value2 = e.target.value;
- $settingNote.textContent = value2 === "1080p" ? "✅ " + t("can-stream-xbox-360-games") : "❌ " + t("cant-stream-xbox-360-games"), setPref("xhome_resolution", value2);
+ $settingNote.textContent = value2 === "1080p" ? "✅ " + t("can-stream-xbox-360-games") : "❌ " + t("cant-stream-xbox-360-games"), setPref(PrefKey.REMOTE_PLAY_RESOLUTION, value2);
});
const $label = CE("label", {
for: id2,
@@ -5924,7 +6255,7 @@ class RemotePlay {
}
static play(serverId, resolution) {
if (resolution)
- setPref("xhome_resolution", resolution);
+ setPref(PrefKey.REMOTE_PLAY_RESOLUTION, resolution);
STATES.remotePlay.config = {
serverId
}, window.BX_REMOTE_PLAY_CONFIG = STATES.remotePlay.config, localRedirect("/launch/fortnite/BT5P2X999VH2#remote-play"), RemotePlay.detachPopup();
@@ -5937,7 +6268,7 @@ class RemotePlay {
$popup && $popup.remove();
}
static togglePopup(force = null) {
- if (!getPref("xhome_enabled") || !RemotePlay.isReady()) {
+ if (!getPref(PrefKey.REMOTE_PLAY_ENABLED) || !RemotePlay.isReady()) {
Toast.show(t("getting-consoles-list"));
return;
}
@@ -5956,7 +6287,7 @@ class RemotePlay {
RemotePlay.#$content.setAttribute("data-group", group), RemotePlay.#$content.classList.add("bx-remote-play-popup"), RemotePlay.#$content.classList.remove("bx-gone"), $header.insertAdjacentElement("afterend", RemotePlay.#$content);
}
static detect() {
- if (!getPref("xhome_enabled"))
+ if (!getPref(PrefKey.REMOTE_PLAY_ENABLED))
return;
if (STATES.remotePlay.isPlaying = window.location.pathname.includes("/launch/") && window.location.hash.startsWith("#remote-play"), STATES.remotePlay?.isPlaying)
window.BX_REMOTE_PLAY_CONFIG = STATES.remotePlay.config, window.history.replaceState({ origin: "better-xcloud" }, "", "https://www.xbox.com/" + location.pathname.substring(1, 6) + "/play");
@@ -5999,7 +6330,7 @@ class XhomeInterceptor {
}
static async#handleInputConfigs(request, opts) {
const response = await NATIVE_FETCH(request);
- if (getPref("stream_touch_controller") !== "all")
+ if (getPref(PrefKey.STREAM_TOUCH_CONTROLLER) !== "all")
return response;
const obj = await response.clone().json(), xboxTitleId = JSON.parse(opts.body).titleIds[0];
TouchController.setXboxTitleId(xboxTitleId);
@@ -6007,7 +6338,7 @@ class XhomeInterceptor {
let hasTouchSupport = inputConfigs.supportedTabs.length > 0;
if (!hasTouchSupport) {
const supportedInputTypes = inputConfigs.supportedInputTypes;
- hasTouchSupport = supportedInputTypes.includes("NativeTouch") || supportedInputTypes.includes("CustomTouchOverlay");
+ hasTouchSupport = supportedInputTypes.includes(SupportedInputType.NATIVE_TOUCH) || supportedInputTypes.includes(SupportedInputType.CUSTOM_TOUCH_OVERLAY);
}
if (hasTouchSupport)
TouchController.disable(), BxEvent.dispatch(window, BxEvent.CUSTOM_TOUCH_LAYOUTS_LOADED, {
@@ -6042,7 +6373,7 @@ class XhomeInterceptor {
headers[pair[0]] = pair[1];
headers.authorization = `Bearer ${RemotePlay.XHOME_TOKEN}`;
const deviceInfo = RemotePlay.BASE_DEVICE_INFO;
- if (getPref("xhome_resolution") === "720p")
+ if (getPref(PrefKey.REMOTE_PLAY_RESOLUTION) === "720p")
deviceInfo.dev.os.name = "android";
headers["x-ms-device-info"] = JSON.stringify(deviceInfo);
const opts = {
@@ -6091,7 +6422,7 @@ class LoadingScreen {
const $bgStyle = CE("style");
document.documentElement.appendChild($bgStyle), LoadingScreen.#$bgStyle = $bgStyle;
}
- if (LoadingScreen.#setBackground(titleInfo.product.heroImageUrl || titleInfo.product.titledHeroImageUrl || titleInfo.product.tileImageUrl), getPref("ui_loading_screen_rocket") === "hide")
+ if (LoadingScreen.#setBackground(titleInfo.product.heroImageUrl || titleInfo.product.titledHeroImageUrl || titleInfo.product.tileImageUrl), getPref(PrefKey.UI_LOADING_SCREEN_ROCKET) === "hide")
LoadingScreen.#hideRocket();
}
static #hideRocket() {
@@ -6134,7 +6465,7 @@ class LoadingScreen {
}, bg.src = imageUrl;
}
static setupWaitTime(waitTime) {
- if (getPref("ui_loading_screen_rocket") === "hide-queue")
+ if (getPref(PrefKey.UI_LOADING_SCREEN_ROCKET) === "hide-queue")
LoadingScreen.#hideRocket();
let secondsLeft = waitTime, $countDown, $estimated;
LoadingScreen.#orgWebTitle = document.title;
@@ -6153,7 +6484,7 @@ class LoadingScreen {
}, 1000);
}
static hide() {
- if (LoadingScreen.#orgWebTitle && (document.title = LoadingScreen.#orgWebTitle), LoadingScreen.#$waitTimeBox && LoadingScreen.#$waitTimeBox.classList.add("bx-gone"), getPref("ui_loading_screen_game_art") && LoadingScreen.#$bgStyle) {
+ if (LoadingScreen.#orgWebTitle && (document.title = LoadingScreen.#orgWebTitle), LoadingScreen.#$waitTimeBox && LoadingScreen.#$waitTimeBox.classList.add("bx-gone"), getPref(PrefKey.UI_LOADING_SCREEN_GAME_ART) && LoadingScreen.#$bgStyle) {
const $rocketBg = document.querySelector('#game-stream rect[width="800"]');
$rocketBg && $rocketBg.addEventListener("transitionend", (e) => {
LoadingScreen.#$bgStyle.textContent += `
@@ -6174,14 +6505,24 @@ class LoadingScreen {
}
}
+var StreamBadge;
+(function(StreamBadge2) {
+ StreamBadge2["PLAYTIME"] = "playtime";
+ StreamBadge2["BATTERY"] = "battery";
+ StreamBadge2["DOWNLOAD"] = "in";
+ StreamBadge2["UPLOAD"] = "out";
+ StreamBadge2["SERVER"] = "server";
+ StreamBadge2["VIDEO"] = "video";
+ StreamBadge2["AUDIO"] = "audio";
+})(StreamBadge || (StreamBadge = {}));
var StreamBadgeIcon = {
- ["playtime"]: BxIcon.PLAYTIME,
- ["video"]: BxIcon.DISPLAY,
- ["battery"]: BxIcon.BATTERY,
- ["in"]: BxIcon.DOWNLOAD,
- ["out"]: BxIcon.UPLOAD,
- ["server"]: BxIcon.SERVER,
- ["audio"]: BxIcon.AUDIO
+ [StreamBadge.PLAYTIME]: BxIcon.PLAYTIME,
+ [StreamBadge.VIDEO]: BxIcon.DISPLAY,
+ [StreamBadge.BATTERY]: BxIcon.BATTERY,
+ [StreamBadge.DOWNLOAD]: BxIcon.DOWNLOAD,
+ [StreamBadge.UPLOAD]: BxIcon.UPLOAD,
+ [StreamBadge.SERVER]: BxIcon.SERVER,
+ [StreamBadge.AUDIO]: BxIcon.AUDIO
};
class StreamBadges {
@@ -6209,7 +6550,7 @@ class StreamBadges {
let $badge;
if (this.#cachedDoms[name])
return $badge = this.#cachedDoms[name], $badge.lastElementChild.textContent = value, $badge;
- if ($badge = CE("div", { class: "bx-badge", title: t(`badge-${name}`) }, CE("span", { class: "bx-badge-name" }, createSvgIcon(StreamBadgeIcon[name])), CE("span", { class: "bx-badge-value", style: `background-color: ${color}` }, value)), name === "battery")
+ if ($badge = CE("div", { class: "bx-badge", title: t(`badge-${name}`) }, CE("span", { class: "bx-badge-name" }, createSvgIcon(StreamBadgeIcon[name])), CE("span", { class: "bx-badge-value", style: `background-color: ${color}` }, value)), name === StreamBadge.BATTERY)
$badge.classList.add("bx-badge-battery");
return this.#cachedDoms[name] = $badge, $badge;
}
@@ -6237,10 +6578,10 @@ class StreamBadges {
totalIn += stat.bytesReceived, totalOut += stat.bytesSent;
});
const badges = {
- ["in"]: totalIn ? this.#humanFileSize(totalIn) : null,
- ["out"]: totalOut ? this.#humanFileSize(totalOut) : null,
- ["playtime"]: playtime,
- ["battery"]: batteryLevel
+ [StreamBadge.DOWNLOAD]: totalIn ? this.#humanFileSize(totalIn) : null,
+ [StreamBadge.UPLOAD]: totalOut ? this.#humanFileSize(totalOut) : null,
+ [StreamBadge.PLAYTIME]: playtime,
+ [StreamBadge.BATTERY]: batteryLevel
};
let name;
for (name in badges) {
@@ -6248,7 +6589,7 @@ class StreamBadges {
if (value === null)
continue;
const $elm = this.#cachedDoms[name];
- if ($elm && ($elm.lastElementChild.textContent = value), name === "battery")
+ if ($elm && ($elm.lastElementChild.textContent = value), name === StreamBadge.BATTERY)
if (this.startBatteryLevel === 100 && batteryLevelInt === 100)
$elm.classList.add("bx-gone");
else
@@ -6304,13 +6645,13 @@ class StreamBadges {
let server = this.#region;
server += "@" + (this.#ipv6 ? "IPv6" : "IPv4");
const BADGES = [
- ["playtime", "1m", "#ff004d"],
- ["battery", batteryLevel, "#00b543"],
- ["in", this.#humanFileSize(0), "#29adff"],
- ["out", this.#humanFileSize(0), "#ff77a8"],
- ["server", server, "#ff6c24"],
- video ? ["video", video, "#742f29"] : null,
- audio ? ["audio", audio, "#5f574f"] : null
+ [StreamBadge.PLAYTIME, "1m", "#ff004d"],
+ [StreamBadge.BATTERY, batteryLevel, "#00b543"],
+ [StreamBadge.DOWNLOAD, this.#humanFileSize(0), "#29adff"],
+ [StreamBadge.UPLOAD, this.#humanFileSize(0), "#ff77a8"],
+ [StreamBadge.SERVER, server, "#ff6c24"],
+ video ? [StreamBadge.VIDEO, video, "#742f29"] : null,
+ audio ? [StreamBadge.AUDIO, audio, "#5f574f"] : null
], $container = CE("div", { class: "bx-badges" });
return BADGES.forEach((item2) => {
if (!item2)
@@ -6381,7 +6722,7 @@ class StreamBadges {
class XcloudInterceptor {
static async#handleLogin(request, init) {
- const bypassServer = getPref("server_bypass_restriction");
+ const bypassServer = getPref(PrefKey.SERVER_BYPASS_RESTRICTION);
if (bypassServer !== "off") {
const ip = BypassServerIps[bypassServer];
ip && request.headers.set("X-Forwarded-For", ip);
@@ -6428,7 +6769,7 @@ class XcloudInterceptor {
return STATES.gsToken = obj.gsToken, response.json = () => Promise.resolve(obj), response;
}
static async#handlePlay(request, init) {
- const PREF_STREAM_TARGET_RESOLUTION = getPref("stream_target_resolution"), PREF_STREAM_PREFERRED_LOCALE = getPref("stream_preferred_locale"), url = typeof request === "string" ? request : request.url, parsedUrl = new URL(url);
+ const PREF_STREAM_TARGET_RESOLUTION = getPref(PrefKey.STREAM_TARGET_RESOLUTION), PREF_STREAM_PREFERRED_LOCALE = getPref(PrefKey.STREAM_PREFERRED_LOCALE), url = typeof request === "string" ? request : request.url, parsedUrl = new URL(url);
let badgeRegion = parsedUrl.host.split(".", 1)[0];
for (let regionName in STATES.serverRegions) {
const region4 = STATES.serverRegions[regionName];
@@ -6452,7 +6793,7 @@ class XcloudInterceptor {
}
static async#handleWaitTime(request, init) {
const response = await NATIVE_FETCH(request, init);
- if (getPref("ui_loading_screen_wait_time")) {
+ if (getPref(PrefKey.UI_LOADING_SCREEN_WAIT_TIME)) {
const json = await response.clone().json();
if (json.estimatedAllocationTimeInSeconds > 0)
LoadingScreen.setupWaitTime(json.estimatedTotalWaitTimeInSeconds);
@@ -6462,7 +6803,7 @@ class XcloudInterceptor {
static async#handleConfiguration(request, init) {
if (request.method !== "GET")
return NATIVE_FETCH(request, init);
- if (getPref("stream_touch_controller") === "all")
+ if (getPref(PrefKey.STREAM_TOUCH_CONTROLLER) === "all")
if (STATES.currentStream.titleInfo?.details.hasTouchSupport)
TouchController.disable();
else
@@ -6474,9 +6815,9 @@ class XcloudInterceptor {
let overrides = JSON.parse(obj.clientStreamingConfigOverrides || "{}") || {};
overrides.inputConfiguration = overrides.inputConfiguration || {}, overrides.inputConfiguration.enableVibration = !0;
let overrideMkb = null;
- if (getPref("native_mkb_enabled") === "on" || STATES.currentStream.titleInfo && BX_FLAGS.ForceNativeMkbTitles?.includes(STATES.currentStream.titleInfo.details.productId))
+ if (getPref(PrefKey.NATIVE_MKB_ENABLED) === "on" || STATES.currentStream.titleInfo && BX_FLAGS.ForceNativeMkbTitles?.includes(STATES.currentStream.titleInfo.details.productId))
overrideMkb = !0;
- if (getPref("native_mkb_enabled") === "off")
+ if (getPref(PrefKey.NATIVE_MKB_ENABLED) === "off")
overrideMkb = !1;
if (overrideMkb !== null)
overrides.inputConfiguration = Object.assign(overrides.inputConfiguration, {
@@ -6485,7 +6826,7 @@ class XcloudInterceptor {
});
if (TouchController.isEnabled())
overrides.inputConfiguration.enableTouchInput = !0, overrides.inputConfiguration.maxTouchPoints = 10;
- if (getPref("audio_mic_on_playing"))
+ if (getPref(PrefKey.AUDIO_MIC_ON_PLAYING))
overrides.audioConfiguration = overrides.audioConfiguration || {}, overrides.audioConfiguration.enableMicrophone = !0;
return obj.clientStreamingConfigOverrides = JSON.stringify(overrides), response.json = () => Promise.resolve(obj), response.text = () => Promise.resolve(JSON.stringify(obj)), response;
}
@@ -6505,10 +6846,9 @@ class XcloudInterceptor {
}
}
-function clearApplicationInsightsBuffers() {
+var clearApplicationInsightsBuffers = function() {
window.sessionStorage.removeItem("AI_buffer"), window.sessionStorage.removeItem("AI_sentBuffer");
-}
-function clearDbLogs(dbName, table) {
+}, clearDbLogs = function(dbName, table) {
const request = window.indexedDB.open(dbName);
request.onsuccess = (e) => {
const db = e.target.result;
@@ -6520,11 +6860,9 @@ function clearDbLogs(dbName, table) {
} catch (ex) {
}
};
-}
-function clearAllLogs() {
+}, clearAllLogs = function() {
clearApplicationInsightsBuffers(), clearDbLogs("StreamClientLogHandler", "logs"), clearDbLogs("XCloudAppLogs", "logs");
-}
-function updateIceCandidates(candidates, options) {
+}, updateIceCandidates = function(candidates, options) {
const pattern = new RegExp(/a=candidate:(?\d+) (?\d+) UDP (?\d+) (?[^\s]+) (?\d+) (?.*)/), lst = [];
for (let item2 of candidates) {
if (item2.candidate == "a=end-of-candidates")
@@ -6555,13 +6893,13 @@ function updateIceCandidates(candidates, options) {
newCandidates.push(newCandidate(`a=candidate:${newCandidates.length + 1} 1 UDP 1 ${ip} ${port} typ host`));
}
return newCandidates.push(newCandidate("a=end-of-candidates")), BxLogger.info("ICE Candidates", newCandidates), newCandidates;
-}
+};
async function patchIceCandidates(request, consoleAddrs) {
const response = await NATIVE_FETCH(request), text = await response.clone().text();
if (!text.length)
return response;
const options = {
- preferIpv6Server: getPref("prefer_ipv6_server"),
+ preferIpv6Server: getPref(PrefKey.PREFER_IPV6_SERVER),
consoleAddrs
}, obj = JSON.parse(text);
let exchangeResponse = JSON.parse(obj.exchangeResponse);
@@ -6569,14 +6907,14 @@ async function patchIceCandidates(request, consoleAddrs) {
}
function interceptHttpRequests() {
let BLOCKED_URLS = [];
- if (getPref("block_tracking"))
+ if (getPref(PrefKey.BLOCK_TRACKING))
clearAllLogs(), BLOCKED_URLS = BLOCKED_URLS.concat([
"https://arc.msn.com",
"https://browser.events.data.microsoft.com",
"https://dc.services.visualstudio.com",
"https://2c06dea3f26c40c69b8456d319791fd0@o427368.ingest.sentry.io"
]);
- if (getPref("block_social_features"))
+ if (getPref(PrefKey.BLOCK_SOCIAL_FEATURES))
BLOCKED_URLS = BLOCKED_URLS.concat([
"https://peoplehub.xboxlive.com/users/me/people/social",
"https://peoplehub.xboxlive.com/users/me/people/recommendations",
@@ -6621,10 +6959,10 @@ function interceptHttpRequests() {
}
if (STATES.userAgent.capabilities.touch && url.includes("catalog.gamepass.com/sigls/")) {
const response = await NATIVE_FETCH(request, init), obj = await response.clone().json();
- if (url.includes("29a81209-df6f-41fd-a528-2ae6b91f719c"))
+ if (url.includes(GamePassCloudGallery.ALL))
for (let i = 1;i < obj.length; i++)
gamepassAllGames.push(obj[i].id);
- else if (url.includes("9c86f07a-f3e8-45ad-82a0-a1f759597059"))
+ else if (url.includes(GamePassCloudGallery.TOUCH))
try {
let customList = TouchController.getCustomList();
customList = customList.filter((id2) => gamepassAllGames.includes(id2));
@@ -6635,7 +6973,7 @@ function interceptHttpRequests() {
}
return response.json = () => Promise.resolve(obj), response;
}
- if (BX_FLAGS.ForceNativeMkbTitles && url.includes("catalog.gamepass.com/sigls/") && url.includes("8fa264dd-124f-4af3-97e8-596fcdf4b486")) {
+ if (BX_FLAGS.ForceNativeMkbTitles && url.includes("catalog.gamepass.com/sigls/") && url.includes(GamePassCloudGallery.NATIVE_MKB)) {
const response = await NATIVE_FETCH(request, init), obj = await response.clone().json();
try {
const newCustomList = BX_FLAGS.ForceNativeMkbTitles.map((item2) => ({ id: item2 }));
@@ -6661,7 +6999,7 @@ function showGamepadToast(gamepad) {
return;
BxLogger.info("Gamepad", gamepad);
let text = "🎮";
- if (getPref("local_co_op_enabled"))
+ if (getPref(PrefKey.LOCAL_CO_OP_ENABLED))
text += ` #${gamepad.index + 1}`;
const gamepadId = gamepad.id.replace(/ \(.*?Vendor: \w+ Product: \w+\)$/, "");
text += ` - ${gamepadId}`;
@@ -6674,29 +7012,29 @@ function showGamepadToast(gamepad) {
}
function addCss() {
- let css = `:root{--bx-title-font:Bahnschrift,Arial,Helvetica,sans-serif;--bx-title-font-semibold:Bahnschrift Semibold,Arial,Helvetica,sans-serif;--bx-normal-font:"Segoe UI",Arial,Helvetica,sans-serif;--bx-monospaced-font:Consolas,"Courier New",Courier,monospace;--bx-promptfont-font:promptfont;--bx-button-height:40px;--bx-default-button-color:#2d3036;--bx-default-button-rgb:45,48,54;--bx-default-button-hover-color:#515863;--bx-default-button-hover-rgb:81,88,99;--bx-default-button-active-color:#222428;--bx-default-button-active-rgb:34,36,40;--bx-default-button-disabled-color:#8e8e8e;--bx-default-button-disabled-rgb:142,142,142;--bx-primary-button-color:#008746;--bx-primary-button-rgb:0,135,70;--bx-primary-button-hover-color:#04b358;--bx-primary-button-hover-rgb:4,179,88;--bx-primary-button-active-color:#044e2a;--bx-primary-button-active-rgb:4,78,42;--bx-primary-button-disabled-color:#448262;--bx-primary-button-disabled-rgb:68,130,98;--bx-danger-button-color:#c10404;--bx-danger-button-rgb:193,4,4;--bx-danger-button-hover-color:#e61d1d;--bx-danger-button-hover-rgb:230,29,29;--bx-danger-button-active-color:#a26c6c;--bx-danger-button-active-rgb:162,108,108;--bx-danger-button-disabled-color:#df5656;--bx-danger-button-disabled-rgb:223,86,86;--bx-toast-z-index:9999;--bx-dialog-z-index:9101;--bx-dialog-overlay-z-index:9100;--bx-stats-bar-z-index:9010;--bx-mkb-pointer-lock-msg-z-index:9000;--bx-navigation-dialog-z-index:8999;--bx-navigation-dialog-overlay-z-index:8998;--bx-remote-play-popup-z-index:2000;--bx-game-bar-z-index:1000;--bx-wait-time-box-z-index:100;--bx-screenshot-animation-z-index:1}@font-face{font-family:'promptfont';src:url("https://redphx.github.io/better-xcloud/fonts/promptfont.otf")}div[class^=HUDButton-module__hiddenContainer] ~ div:not([class^=HUDButton-module__hiddenContainer]){opacity:0;pointer-events:none !important;position:absolute;top:-9999px;left:-9999px}@media screen and (max-width:600px){header a[href="/play"]{display:none}}.bx-full-width{width:100% !important}.bx-full-height{height:100% !important}.bx-no-scroll{overflow:hidden !important}.bx-hide-scroll-bar{scrollbar-width:none}.bx-hide-scroll-bar::-webkit-scrollbar{display:none}.bx-gone{display:none !important}.bx-offscreen{position:absolute !important;top:-9999px !important;left:-9999px !important;visibility:hidden !important}.bx-hidden{visibility:hidden !important}.bx-invisible{opacity:0}.bx-unclickable{pointer-events:none}.bx-pixel{width:1px !important;height:1px !important}.bx-no-margin{margin:0 !important}.bx-no-padding{padding:0 !important}.bx-prompt{font-family:var(--bx-promptfont-font)}.bx-line-through{text-decoration:line-through !important}.bx-normal-case{text-transform:none !important}select[multiple]{overflow:auto}#headerArea,#uhfSkipToMain,.uhf-footer{display:none}div[class*=NotFocusedDialog]{position:absolute !important;top:-9999px !important;left:-9999px !important;width:0 !important;height:0 !important}#game-stream video:not([src]){visibility:hidden}div[class*=SupportedInputsBadge]:not(:has(:nth-child(2))),div[class*=SupportedInputsBadge] svg:first-of-type{display:none}.bx-game-tile-wait-time{position:absolute;top:0;left:0;z-index:1;background:rgba(0,0,0,0.549);display:none;border-radius:0 0 4px 0;align-items:center;padding:4px 8px}a[class^=BaseItem-module__container]:focus .bx-game-tile-wait-time,button[class^=BaseItem-module__container]:focus .bx-game-tile-wait-time{display:flex}.bx-game-tile-wait-time svg{width:14px;height:16px;margin-right:2px}.bx-game-tile-wait-time span{display:inline-block;height:16px;line-height:16px;font-size:12px;font-weight:bold}.bx-button{--button-rgb:var(--bx-default-button-rgb);--button-hover-rgb:var(--bx-default-button-hover-rgb);--button-active-rgb:var(--bx-default-button-active-rgb);--button-disabled-rgb:var(--bx-default-button-disabled-rgb);background-color:rgb(var(--button-rgb));user-select:none;-webkit-user-select:none;color:#fff;font-family:var(--bx-title-font-semibold);font-size:14px;border:none;font-weight:400;height:var(--bx-button-height);border-radius:4px;padding:0 8px;text-transform:uppercase;cursor:pointer;overflow:hidden}.bx-button:not([disabled]):active{background-color:rgb(var(--button-active-rgb))}.bx-button:focus{outline:none !important}.bx-button:not([disabled]):not(:active):hover,.bx-button:not([disabled]):not(:active).bx-focusable:focus{background-color:rgb(var(--button-hover-rgb))}.bx-button:disabled{cursor:default;background-color:rgb(var(--button-disabled-rgb))}.bx-button.bx-ghost{background-color:transparent}.bx-button.bx-ghost:not([disabled]):not(:active):hover,.bx-button.bx-ghost:not([disabled]):not(:active).bx-focusable:focus{background-color:rgb(var(--button-hover-rgb))}.bx-button.bx-primary{--button-rgb:var(--bx-primary-button-rgb)}.bx-button.bx-primary:not([disabled]):active{--button-active-rgb:var(--bx-primary-button-active-rgb)}.bx-button.bx-primary:not([disabled]):not(:active):hover,.bx-button.bx-primary:not([disabled]):not(:active).bx-focusable:focus{--button-hover-rgb:var(--bx-primary-button-hover-rgb)}.bx-button.bx-primary:disabled{--button-disabled-rgb:var(--bx-primary-button-disabled-rgb)}.bx-button.bx-danger{--button-rgb:var(--bx-danger-button-rgb)}.bx-button.bx-danger:not([disabled]):active{--button-active-rgb:var(--bx-danger-button-active-rgb)}.bx-button.bx-danger:not([disabled]):not(:active):hover,.bx-button.bx-danger:not([disabled]):not(:active).bx-focusable:focus{--button-hover-rgb:var(--bx-danger-button-hover-rgb)}.bx-button.bx-danger:disabled{--button-disabled-rgb:var(--bx-danger-button-disabled-rgb)}.bx-button.bx-frosted{--button-alpha:.2;background-color:rgba(var(--button-rgb), var(--button-alpha));backdrop-filter:blur(4px) brightness(1.5)}.bx-button.bx-frosted:not([disabled]):not(:active):hover,.bx-button.bx-frosted:not([disabled]):not(:active).bx-focusable:focus{background-color:rgba(var(--button-hover-rgb), var(--button-alpha))}.bx-button.bx-drop-shadow{box-shadow:0 0 4px rgba(0,0,0,0.502)}.bx-button.bx-tall{height:calc(var(--bx-button-height) * 1.5) !important}.bx-button.bx-circular{border-radius:var(--bx-button-height);height:var(--bx-button-height)}.bx-button svg{display:inline-block;width:16px;height:var(--bx-button-height)}.bx-button span{display:inline-block;line-height:var(--bx-button-height);vertical-align:middle;color:#fff;overflow:hidden;white-space:nowrap}.bx-button span:not(:only-child){margin-left:10px}.bx-focusable{position:relative;overflow:visible}.bx-focusable::after{border:2px solid transparent;border-radius:10px}.bx-focusable:focus-visible::after{content:'';border-color:#fff;position:absolute;top:-6px;left:-6px;right:-6px;bottom:-6px}.bx-focusable.bx-circular::after{border-radius:var(--bx-button-height)}a.bx-button{display:inline-block}a.bx-button.bx-full-width{text-align:center}button.bx-inactive{pointer-events:none;opacity:.2;background:transparent !important}.bx-button-shortcut{max-width:max-content;margin:10px 0 0 0;overflow:hidden}@media (min-width:568px) and (max-height:480px){.bx-button-shortcut{margin:8px 0 0 10px}}.bx-header-remote-play-button{height:auto;margin-right:8px !important}.bx-header-remote-play-button svg{width:24px;height:24px}.bx-header-settings-button{line-height:30px;font-size:14px;text-transform:uppercase;position:relative}.bx-header-settings-button[data-update-available]::before{content:'🌟' !important;line-height:var(--bx-button-height);display:inline-block;margin-left:4px}.bx-dialog-overlay{position:fixed;inset:0;z-index:var(--bx-dialog-overlay-z-index);background:#000;opacity:50%}.bx-dialog{display:flex;flex-flow:column;max-height:90vh;position:fixed;top:50%;left:50%;margin-right:-50%;transform:translate(-50%,-50%);min-width:420px;padding:20px;border-radius:8px;z-index:var(--bx-dialog-z-index);background:#1a1b1e;color:#fff;font-weight:400;font-size:16px;font-family:var(--bx-normal-font);box-shadow:0 0 6px #000;user-select:none;-webkit-user-select:none}.bx-dialog *:focus{outline:none !important}.bx-dialog h2{display:flex;margin-bottom:12px}.bx-dialog h2 b{flex:1;color:#fff;display:block;font-family:var(--bx-title-font);font-size:26px;font-weight:400;line-height:var(--bx-button-height)}.bx-dialog.bx-binding-dialog h2 b{font-family:var(--bx-promptfont-font) !important}.bx-dialog > div{overflow:auto;padding:2px 0}.bx-dialog > button{padding:8px 32px;margin:10px auto 0;border:none;border-radius:4px;display:block;background-color:#2d3036;text-align:center;color:#fff;text-transform:uppercase;font-family:var(--bx-title-font);font-weight:400;line-height:18px;font-size:14px}@media (hover:hover){.bx-dialog > button:hover{background-color:#515863}}.bx-dialog > button:focus{background-color:#515863}@media screen and (max-width:450px){.bx-dialog{min-width:100%}}.bx-navigation-dialog{position:absolute;z-index:var(--bx-navigation-dialog-z-index)}.bx-navigation-dialog-overlay{position:fixed;background:rgba(11,11,11,0.89);top:0;left:0;right:0;bottom:0;z-index:var(--bx-navigation-dialog-overlay-z-index)}.bx-navigation-dialog-overlay[data-is-playing="true"]{background:transparent}.bx-settings-dialog{display:flex;position:fixed;top:0;right:0;bottom:0;opacity:.98;user-select:none;-webkit-user-select:none}.bx-settings-dialog .bx-focusable::after{border-radius:4px}.bx-settings-dialog .bx-focusable:focus::after{top:0;left:0;right:0;bottom:0}.bx-settings-dialog .bx-settings-reload-note{font-size:.8rem;display:block;padding:8px;font-style:italic;font-weight:normal;height:var(--bx-button-height)}.bx-settings-tabs-container{position:fixed;width:48px;max-height:100vh;display:flex;flex-direction:column}.bx-settings-tabs-container > div:last-of-type{display:flex;flex-direction:column;align-items:end}.bx-settings-tabs-container > div:last-of-type button{flex-shrink:0;border-top-right-radius:0;border-bottom-right-radius:0;margin-top:8px;height:unset;padding:8px 10px}.bx-settings-tabs-container > div:last-of-type button svg{width:16px;height:16px}.bx-settings-tabs{display:flex;flex-direction:column;border-radius:0 0 0 8px;box-shadow:0 0 6px #000;overflow:overlay;flex:1}.bx-settings-tabs svg{width:24px;height:24px;padding:10px;flex-shrink:0;box-sizing:content-box;background:#131313;cursor:pointer;border-left:4px solid #1e1e1e}.bx-settings-tabs svg.bx-active{background:#222;border-color:#008746}.bx-settings-tabs svg:not(.bx-active):hover{background:#2f2f2f;border-color:#484848}.bx-settings-tabs svg:focus{border-color:#fff;outline:none}.bx-settings-tabs svg[data-group=global][data-need-refresh=true]{background:var(--bx-danger-button-color) !important}.bx-settings-tabs svg[data-group=global][data-need-refresh=true]:hover{background:var(--bx-danger-button-hover-color) !important}.bx-settings-tab-contents{flex-direction:column;padding:10px;margin-left:48px;width:450px;max-width:calc(100vw - tabsWidth);background:#1a1b1e;color:#fff;font-weight:400;font-size:16px;font-family:var(--bx-title-font);text-align:center;box-shadow:0 0 6px #000;overflow:overlay;z-index:1}.bx-settings-tab-contents > div[data-tab-group=mkb]{display:flex;flex-direction:column;height:100%;overflow:hidden}.bx-settings-tab-contents > div[data-tab-group=shortcuts] > div[data-has-gamepad=true] > div:first-of-type{display:none}.bx-settings-tab-contents > div[data-tab-group=shortcuts] > div[data-has-gamepad=true] > div:last-of-type{display:block}.bx-settings-tab-contents > div[data-tab-group=shortcuts] > div[data-has-gamepad=false] > div:first-of-type{display:block}.bx-settings-tab-contents > div[data-tab-group=shortcuts] > div[data-has-gamepad=false] > div:last-of-type{display:none}.bx-settings-tab-contents > div[data-tab-group=shortcuts] .bx-shortcut-profile{width:100%;height:36px;display:block}.bx-settings-tab-contents > div[data-tab-group=shortcuts] .bx-shortcut-note{margin-top:10px;font-size:14px}.bx-settings-tab-contents > div[data-tab-group=shortcuts] .bx-shortcut-row{display:flex;margin-bottom:10px}.bx-settings-tab-contents > div[data-tab-group=shortcuts] .bx-shortcut-row label.bx-prompt{flex:1;font-size:26px;margin-bottom:0}.bx-settings-tab-contents > div[data-tab-group=shortcuts] .bx-shortcut-row .bx-shortcut-actions{flex:2;position:relative}.bx-settings-tab-contents > div[data-tab-group=shortcuts] .bx-shortcut-row .bx-shortcut-actions select{position:absolute;width:100%;height:100%;display:block}.bx-settings-tab-contents > div[data-tab-group=shortcuts] .bx-shortcut-row .bx-shortcut-actions select:last-of-type{opacity:0;z-index:calc(var(--bx-settings-z-index) + 1)}.bx-settings-tab-contents:focus,.bx-settings-tab-contents *:focus{outline:none !important}.bx-settings-tab-contents .bx-top-buttons{display:flex;flex-direction:column;gap:8px;margin-bottom:8px}.bx-settings-tab-contents .bx-top-buttons .bx-button{display:block}.bx-settings-tab-contents h2{margin:16px 0 8px 0;display:flex;align-items:center}.bx-settings-tab-contents h2:first-of-type{margin-top:0}.bx-settings-tab-contents h2 span{display:inline-block;font-size:20px;font-weight:bold;text-align:left;flex:1;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}@media (max-width:500px){.bx-settings-tab-contents{width:calc(100vw - 48px)}}.bx-settings-row{display:flex;gap:10px;border-bottom:1px solid #2c2c2e;padding:16px 8px;margin:0;border-left:2px solid transparent}.bx-settings-row:hover,.bx-settings-row:focus-within{background-color:#242424}.bx-settings-row:not(:has(> input[type=checkbox])){flex-wrap:wrap}.bx-settings-row input[type=checkbox]:focus,.bx-settings-row select:focus{filter:drop-shadow(1px 0 0 #fff) drop-shadow(-1px 0 0 #fff) drop-shadow(0 1px 0 #fff) drop-shadow(0 -1px 0 #fff)}.bx-settings-row:has(input:focus),.bx-settings-row:has(select:focus),.bx-settings-row:has(button:focus){border-left-color:#fff}.bx-settings-row > span.bx-settings-label{font-size:14px;display:block;text-align:left;align-self:center;margin-bottom:0 !important}.bx-settings-row > span.bx-settings-label + *{margin:0 0 0 auto}.bx-settings-row input{accent-color:var(--bx-primary-button-color)}.bx-settings-row input:focus{accent-color:var(--bx-danger-button-color)}.bx-settings-row select:disabled{-webkit-appearance:none;background:transparent;text-align-last:right;border:none;color:#fff}.bx-settings-row select option:disabled{display:none}.bx-settings-dialog-note{display:block;color:#afafb0;font-size:12px;font-weight:lighter;font-style:italic}.bx-settings-dialog-note:not(:has(a)){margin-top:4px}.bx-settings-dialog-note a{display:inline-block;padding:4px}.bx-settings-custom-user-agent{display:block;width:100%;padding:6px}.bx-donation-link{display:block;text-align:center;text-decoration:none;height:20px;line-height:20px;font-size:14px;margin-top:10px;color:#5dc21e}.bx-donation-link:hover{color:#6dd72b}.bx-donation-link:focus{text-decoration:underline}.bx-debug-info button{margin-top:10px}.bx-debug-info pre{margin-top:10px;cursor:copy;color:#fff;padding:8px;border:1px solid #2d2d2d;background:#212121;white-space:break-spaces;text-align:left}.bx-debug-info pre:hover{background:#272727}.bx-settings-app-version{margin-top:10px;text-align:center;color:#747474;font-size:12px}.bx-note-unsupported{display:block;font-size:12px;font-style:italic;font-weight:normal;color:#828282}.bx-toast{user-select:none;-webkit-user-select:none;position:fixed;left:50%;top:24px;transform:translate(-50%,0);background:#000;border-radius:16px;color:#fff;z-index:var(--bx-toast-z-index);font-family:var(--bx-normal-font);border:2px solid #fff;display:flex;align-items:center;opacity:0;overflow:clip;transition:opacity .2s ease-in}.bx-toast.bx-show{opacity:.85}.bx-toast.bx-hide{opacity:0;pointer-events:none}.bx-toast-msg{font-size:14px;display:inline-block;padding:12px 16px;white-space:pre}.bx-toast-status{font-weight:bold;font-size:14px;text-transform:uppercase;display:inline-block;background:#515863;padding:12px 16px;color:#fff;white-space:pre}.bx-wait-time-box{position:fixed;top:0;right:0;background-color:rgba(0,0,0,0.8);color:#fff;z-index:var(--bx-wait-time-box-z-index);padding:12px;border-radius:0 0 0 8px}.bx-wait-time-box label{display:block;text-transform:uppercase;text-align:right;font-size:12px;font-weight:bold;margin:0}.bx-wait-time-box span{display:block;font-family:var(--bx-monospaced-font);text-align:right;font-size:16px;margin-bottom:10px}.bx-wait-time-box span:last-of-type{margin-bottom:0}.bx-remote-play-popup{width:100%;max-width:1920px;margin:auto;position:relative;height:.1px;overflow:visible;z-index:var(--bx-remote-play-popup-z-index)}.bx-remote-play-container{position:absolute;right:10px;top:0;background:#1a1b1e;border-radius:10px;width:420px;max-width:calc(100vw - 20px);margin:0 0 0 auto;padding:20px;box-shadow:rgba(0,0,0,0.502) 0 0 12px 0}@media (min-width:480px) and (min-height:calc(480px + 1px)){.bx-remote-play-container{right:calc(env(safe-area-inset-right, 0px) + 32px)}}@media (min-width:768px) and (min-height:calc(480px + 1px)){.bx-remote-play-container{right:calc(env(safe-area-inset-right, 0px) + 48px)}}@media (min-width:1920px) and (min-height:calc(480px + 1px)){.bx-remote-play-container{right:calc(env(safe-area-inset-right, 0px) + 80px)}}.bx-remote-play-container > .bx-button{display:table;margin:0 0 0 auto}.bx-remote-play-settings{margin-bottom:12px;padding-bottom:12px;border-bottom:1px solid #2d2d2d}.bx-remote-play-settings > div{display:flex}.bx-remote-play-settings label{flex:1}.bx-remote-play-settings label p{margin:4px 0 0;padding:0;color:#888;font-size:12px}.bx-remote-play-settings span{font-weight:bold;font-size:18px;display:block;margin-bottom:8px;text-align:center}.bx-remote-play-resolution{display:block}.bx-remote-play-resolution input[type="radio"]{accent-color:var(--bx-primary-button-color);margin-right:6px}.bx-remote-play-resolution input[type="radio"]:focus{accent-color:var(--bx-primary-button-hover-color)}.bx-remote-play-device-wrapper{display:flex;margin-bottom:12px}.bx-remote-play-device-wrapper:last-child{margin-bottom:2px}.bx-remote-play-device-info{flex:1;padding:4px 0}.bx-remote-play-device-name{font-size:20px;font-weight:bold;display:inline-block;vertical-align:middle}.bx-remote-play-console-type{font-size:12px;background:#004c87;color:#fff;display:inline-block;border-radius:14px;padding:2px 10px;margin-left:8px;vertical-align:middle}.bx-remote-play-power-state{color:#888;font-size:14px}.bx-remote-play-connect-button{min-height:100%;margin:4px 0}.bx-select{display:flex;align-items:center;flex:0 1 auto}.bx-select select{display:none !important}.bx-select > div,.bx-select button.bx-select-value{min-width:110px;text-align:center;margin:0 8px;line-height:24px;vertical-align:middle;background:#fff;color:#000;border-radius:4px;padding:2px 8px;flex:1}.bx-select > div{display:inline-block}.bx-select > div input{display:inline-block;margin-right:8px}.bx-select > div label{margin-bottom:0;font-size:14px;width:100%}.bx-select > div label span{display:block;font-size:10px;font-weight:bold;text-align:left;line-height:initial}.bx-select button.bx-select-value{border:none;display:inline-flex;cursor:pointer;min-height:30px;font-size:.9rem;align-items:center}.bx-select button.bx-select-value span{flex:1;text-align:center;display:inline-block}.bx-select button.bx-select-value input{margin:0 4px;accent-color:var(--bx-primary-button-color)}.bx-select button.bx-select-value:hover input,.bx-select button.bx-select-value:focus input{accent-color:var(--bx-danger-button-color)}.bx-select button.bx-select-value:hover::after,.bx-select button.bx-select-value:focus::after{border-color:#4d4d4d !important}.bx-select button.bx-button{border:none;height:24px;width:24px;padding:0;line-height:24px;color:#fff;border-radius:4px;font-weight:bold;font-size:12px;font-family:var(--bx-monospaced-font);flex-shrink:0}.bx-select button.bx-button span{line-height:unset}div[class*=StreamMenu-module__menuContainer] > div[class*=Menu-module]{overflow:visible}.bx-stream-menu-button-on{fill:#000 !important;background-color:#2d2d2d !important;color:#000 !important}.bx-stream-refresh-button{top:calc(env(safe-area-inset-top, 0px) + 10px + 50px) !important}body[data-media-type=default] .bx-stream-refresh-button{left:calc(env(safe-area-inset-left, 0px) + 11px) !important}body[data-media-type=tv] .bx-stream-refresh-button{top:calc(var(--gds-focus-borderSize) + 80px) !important}.bx-stream-home-button{top:calc(env(safe-area-inset-top, 0px) + 10px + 50px * 2) !important}body[data-media-type=default] .bx-stream-home-button{left:calc(env(safe-area-inset-left, 0px) + 12px) !important}body[data-media-type=tv] .bx-stream-home-button{top:calc(var(--gds-focus-borderSize) + 80px * 2) !important}div[data-testid=media-container]{display:flex}div[data-testid=media-container].bx-taking-screenshot:before{animation:bx-anim-taking-screenshot .5s ease;content:' ';position:absolute;width:100%;height:100%;z-index:var(--bx-screenshot-animation-z-index)}#game-stream video{margin:auto;align-self:center;background:#000}#game-stream canvas{position:absolute;align-self:center;margin:auto;left:0;right:0}#gamepass-dialog-root div[class^=Guide-module__guide] .bx-button{overflow:visible;margin-bottom:12px}@-moz-keyframes bx-anim-taking-screenshot{0%{border:0 solid rgba(255,255,255,0.502)}50%{border:8px solid rgba(255,255,255,0.502)}100%{border:0 solid rgba(255,255,255,0.502)}}@-webkit-keyframes bx-anim-taking-screenshot{0%{border:0 solid rgba(255,255,255,0.502)}50%{border:8px solid rgba(255,255,255,0.502)}100%{border:0 solid rgba(255,255,255,0.502)}}@-o-keyframes bx-anim-taking-screenshot{0%{border:0 solid rgba(255,255,255,0.502)}50%{border:8px solid rgba(255,255,255,0.502)}100%{border:0 solid rgba(255,255,255,0.502)}}@keyframes bx-anim-taking-screenshot{0%{border:0 solid rgba(255,255,255,0.502)}50%{border:8px solid rgba(255,255,255,0.502)}100%{border:0 solid rgba(255,255,255,0.502)}}.bx-number-stepper{text-align:center}.bx-number-stepper span{display:inline-block;min-width:40px;font-family:var(--bx-monospaced-font);font-size:12px;margin:0 4px}.bx-number-stepper button{border:none;width:24px;height:24px;margin:0;line-height:24px;background-color:var(--bx-default-button-color);color:#fff;border-radius:4px;font-weight:bold;font-size:14px;font-family:var(--bx-monospaced-font)}@media (hover:hover){.bx-number-stepper button:hover{background-color:var(--bx-default-button-hover-color)}}.bx-number-stepper button:active{background-color:var(--bx-default-button-hover-color)}.bx-number-stepper button:disabled + span{font-family:var(--bx-title-font)}.bx-number-stepper input[type="range"]{display:block;margin:12px auto 2px;width:180px;color:#959595 !important}.bx-number-stepper input[type=range]:disabled,.bx-number-stepper button:disabled{display:none}.bx-number-stepper[data-disabled=true] input[type=range],.bx-number-stepper[data-disabled=true] button{display:none}#bx-game-bar{z-index:var(--bx-game-bar-z-index);position:fixed;bottom:0;width:40px;height:90px;overflow:visible;cursor:pointer}#bx-game-bar > svg{display:none;pointer-events:none;position:absolute;height:28px;margin-top:16px}@media (hover:hover){#bx-game-bar:hover > svg{display:block}}#bx-game-bar .bx-game-bar-container{opacity:0;position:absolute;display:flex;overflow:hidden;background:rgba(26,27,30,0.91);box-shadow:0 0 6px #1c1c1c;transition:opacity .1s ease-in}#bx-game-bar .bx-game-bar-container.bx-show{opacity:.9}#bx-game-bar .bx-game-bar-container.bx-show + svg{display:none !important}#bx-game-bar .bx-game-bar-container.bx-hide{opacity:0;pointer-events:none}#bx-game-bar .bx-game-bar-container button{width:60px;height:60px;border-radius:0}#bx-game-bar .bx-game-bar-container button svg{width:28px;height:28px;transition:transform .08s ease 0s}#bx-game-bar .bx-game-bar-container button:hover{border-radius:0}#bx-game-bar .bx-game-bar-container button:active svg{transform:scale(.75)}#bx-game-bar .bx-game-bar-container button.bx-activated{background-color:#fff}#bx-game-bar .bx-game-bar-container button.bx-activated svg{filter:invert(1)}#bx-game-bar .bx-game-bar-container div[data-enabled] button{display:none}#bx-game-bar .bx-game-bar-container div[data-enabled='true'] button:first-of-type{display:block}#bx-game-bar .bx-game-bar-container div[data-enabled='false'] button:last-of-type{display:block}#bx-game-bar[data-position="bottom-left"]{left:0;direction:ltr}#bx-game-bar[data-position="bottom-left"] .bx-game-bar-container{border-radius:0 10px 10px 0}#bx-game-bar[data-position="bottom-right"]{right:0;direction:rtl}#bx-game-bar[data-position="bottom-right"] .bx-game-bar-container{direction:ltr;border-radius:10px 0 0 10px}.bx-badges{margin-left:0;user-select:none;-webkit-user-select:none}.bx-badge{border:none;display:inline-block;line-height:24px;color:#fff;font-family:var(--bx-title-font-semibold);font-size:14px;font-weight:400;margin:0 8px 8px 0;box-shadow:0 0 6px #000;border-radius:4px}.bx-badge-name{background-color:#2d3036;border-radius:4px 0 0 4px}.bx-badge-name svg{width:16px;height:16px}.bx-badge-value{background-color:#808080;border-radius:0 4px 4px 0}.bx-badge-name,.bx-badge-value{display:inline-block;padding:0 8px;line-height:30px;vertical-align:bottom}.bx-badge-battery[data-charging=true] span:first-of-type::after{content:' ⚡️'}div[class^=StreamMenu-module__container] .bx-badges{position:absolute;max-width:500px}#gamepass-dialog-root .bx-badges{position:fixed;top:60px;left:460px;max-width:500px}@media (min-width:568px) and (max-height:480px){#gamepass-dialog-root .bx-badges{position:unset;top:unset;left:unset;margin:8px 0}}.bx-stats-bar{display:block;user-select:none;-webkit-user-select:none;position:fixed;top:0;background-color:#000;color:#fff;font-family:var(--bx-monospaced-font);font-size:.9rem;padding-left:8px;z-index:var(--bx-stats-bar-z-index);text-wrap:nowrap}.bx-stats-bar[data-stats*="[fps]"] > .bx-stat-fps,.bx-stats-bar[data-stats*="[ping]"] > .bx-stat-ping,.bx-stats-bar[data-stats*="[btr]"] > .bx-stat-btr,.bx-stats-bar[data-stats*="[dt]"] > .bx-stat-dt,.bx-stats-bar[data-stats*="[pl]"] > .bx-stat-pl,.bx-stats-bar[data-stats*="[fl]"] > .bx-stat-fl{display:inline-block}.bx-stats-bar[data-stats$="[fps]"] > .bx-stat-fps,.bx-stats-bar[data-stats$="[ping]"] > .bx-stat-ping,.bx-stats-bar[data-stats$="[btr]"] > .bx-stat-btr,.bx-stats-bar[data-stats$="[dt]"] > .bx-stat-dt,.bx-stats-bar[data-stats$="[pl]"] > .bx-stat-pl,.bx-stats-bar[data-stats$="[fl]"] > .bx-stat-fl{margin-right:0;border-right:none}.bx-stats-bar::before{display:none;content:'👀';vertical-align:middle;margin-right:8px}.bx-stats-bar[data-display=glancing]::before{display:inline-block}.bx-stats-bar[data-position=top-left]{left:0;border-radius:0 0 4px 0}.bx-stats-bar[data-position=top-right]{right:0;border-radius:0 0 0 4px}.bx-stats-bar[data-position=top-center]{transform:translate(-50%,0);left:50%;border-radius:0 0 4px 4px}.bx-stats-bar[data-transparent=true]{background:none;filter:drop-shadow(1px 0 0 rgba(0,0,0,0.941)) drop-shadow(-1px 0 0 rgba(0,0,0,0.941)) drop-shadow(0 1px 0 rgba(0,0,0,0.941)) drop-shadow(0 -1px 0 rgba(0,0,0,0.941))}.bx-stats-bar > div{display:none;margin-right:8px;border-right:1px solid #fff;padding-right:8px}.bx-stats-bar label{margin:0 8px 0 0;font-family:var(--bx-title-font);font-size:inherit;font-weight:bold;vertical-align:middle;cursor:help}.bx-stats-bar span{min-width:60px;display:inline-block;text-align:right;vertical-align:middle}.bx-stats-bar span[data-grade=good]{color:#6bffff}.bx-stats-bar span[data-grade=ok]{color:#fff16b}.bx-stats-bar span[data-grade=bad]{color:#ff5f5f}.bx-stats-bar span:first-of-type{min-width:22px}.bx-mkb-settings{display:flex;flex-direction:column;flex:1;padding-bottom:10px;overflow:hidden}.bx-mkb-settings select:disabled{-webkit-appearance:none;background:transparent;text-align-last:right;text-align:right;border:none;color:#fff}.bx-mkb-pointer-lock-msg{user-select:none;-webkit-user-select:none;position:fixed;left:50%;top:50%;transform:translateX(-50%) translateY(-50%);margin:auto;background:#151515;z-index:var(--bx-mkb-pointer-lock-msg-z-index);color:#fff;text-align:center;font-weight:400;font-family:"Segoe UI",Arial,Helvetica,sans-serif;font-size:1.3rem;padding:12px;border-radius:8px;align-items:center;box-shadow:0 0 6px #000;min-width:220px;opacity:.9}.bx-mkb-pointer-lock-msg:hover{opacity:1}.bx-mkb-pointer-lock-msg > div:first-of-type{display:flex;flex-direction:column;text-align:left}.bx-mkb-pointer-lock-msg p{margin:0}.bx-mkb-pointer-lock-msg p:first-child{font-size:22px;margin-bottom:4px;font-weight:bold}.bx-mkb-pointer-lock-msg p:last-child{font-size:12px;font-style:italic}.bx-mkb-pointer-lock-msg > div:last-of-type{margin-top:10px}.bx-mkb-pointer-lock-msg > div:last-of-type[data-type='native'] button:first-of-type{margin-bottom:8px}.bx-mkb-pointer-lock-msg > div:last-of-type[data-type='virtual'] div{display:flex;flex-flow:row;margin-top:8px}.bx-mkb-pointer-lock-msg > div:last-of-type[data-type='virtual'] div button{flex:1}.bx-mkb-pointer-lock-msg > div:last-of-type[data-type='virtual'] div button:first-of-type{margin-right:5px}.bx-mkb-pointer-lock-msg > div:last-of-type[data-type='virtual'] div button:last-of-type{margin-left:5px}.bx-mkb-preset-tools{display:flex;margin-bottom:12px}.bx-mkb-preset-tools select{flex:1}.bx-mkb-preset-tools button{margin-left:6px}.bx-mkb-settings-rows{flex:1;overflow:scroll}.bx-mkb-key-row{display:flex;margin-bottom:10px;align-items:center}.bx-mkb-key-row label{margin-bottom:0;font-family:var(--bx-promptfont-font);font-size:26px;text-align:center;width:26px;height:32px;line-height:32px}.bx-mkb-key-row button{flex:1;height:32px;line-height:32px;margin:0 0 0 10px;background:transparent;border:none;color:#fff;border-radius:0;border-left:1px solid #373737}.bx-mkb-key-row button:hover{background:transparent;cursor:default}.bx-mkb-settings.bx-editing .bx-mkb-key-row button{background:#393939;border-radius:4px;border:none}.bx-mkb-settings.bx-editing .bx-mkb-key-row button:hover{background:#333;cursor:pointer}.bx-mkb-action-buttons > div{text-align:right;display:none}.bx-mkb-action-buttons button{margin-left:8px}.bx-mkb-settings:not(.bx-editing) .bx-mkb-action-buttons > div:first-child{display:block}.bx-mkb-settings.bx-editing .bx-mkb-action-buttons > div:last-child{display:block}.bx-mkb-note{display:block;margin:16px 0 10px;font-size:12px}.bx-mkb-note:first-of-type{margin-top:0}`;
- const PREF_HIDE_SECTIONS = getPref("ui_hide_sections"), selectorToHide = [];
- if (PREF_HIDE_SECTIONS.includes("news"))
+ let css = `:root{--bx-title-font:Bahnschrift,Arial,Helvetica,sans-serif;--bx-title-font-semibold:Bahnschrift Semibold,Arial,Helvetica,sans-serif;--bx-normal-font:"Segoe UI",Arial,Helvetica,sans-serif;--bx-monospaced-font:Consolas,"Courier New",Courier,monospace;--bx-promptfont-font:promptfont;--bx-button-height:40px;--bx-default-button-color:#2d3036;--bx-default-button-rgb:45,48,54;--bx-default-button-hover-color:#515863;--bx-default-button-hover-rgb:81,88,99;--bx-default-button-active-color:#222428;--bx-default-button-active-rgb:34,36,40;--bx-default-button-disabled-color:#8e8e8e;--bx-default-button-disabled-rgb:142,142,142;--bx-primary-button-color:#008746;--bx-primary-button-rgb:0,135,70;--bx-primary-button-hover-color:#04b358;--bx-primary-button-hover-rgb:4,179,88;--bx-primary-button-active-color:#044e2a;--bx-primary-button-active-rgb:4,78,42;--bx-primary-button-disabled-color:#448262;--bx-primary-button-disabled-rgb:68,130,98;--bx-danger-button-color:#c10404;--bx-danger-button-rgb:193,4,4;--bx-danger-button-hover-color:#e61d1d;--bx-danger-button-hover-rgb:230,29,29;--bx-danger-button-active-color:#a26c6c;--bx-danger-button-active-rgb:162,108,108;--bx-danger-button-disabled-color:#df5656;--bx-danger-button-disabled-rgb:223,86,86;--bx-toast-z-index:9999;--bx-dialog-z-index:9101;--bx-dialog-overlay-z-index:9100;--bx-stats-bar-z-index:9010;--bx-mkb-pointer-lock-msg-z-index:9000;--bx-navigation-dialog-z-index:8999;--bx-navigation-dialog-overlay-z-index:8998;--bx-remote-play-popup-z-index:2000;--bx-game-bar-z-index:1000;--bx-wait-time-box-z-index:100;--bx-screenshot-animation-z-index:1}@font-face{font-family:'promptfont';src:url("https://redphx.github.io/better-xcloud/fonts/promptfont.otf")}div[class^=HUDButton-module__hiddenContainer] ~ div:not([class^=HUDButton-module__hiddenContainer]){opacity:0;pointer-events:none !important;position:absolute;top:-9999px;left:-9999px}@media screen and (max-width:600px){header a[href="/play"]{display:none}}.bx-full-width{width:100% !important}.bx-full-height{height:100% !important}.bx-no-scroll{overflow:hidden !important}.bx-hide-scroll-bar{scrollbar-width:none}.bx-hide-scroll-bar::-webkit-scrollbar{display:none}.bx-gone{display:none !important}.bx-offscreen{position:absolute !important;top:-9999px !important;left:-9999px !important;visibility:hidden !important}.bx-hidden{visibility:hidden !important}.bx-invisible{opacity:0}.bx-unclickable{pointer-events:none}.bx-pixel{width:1px !important;height:1px !important}.bx-no-margin{margin:0 !important}.bx-no-padding{padding:0 !important}.bx-prompt{font-family:var(--bx-promptfont-font)}.bx-line-through{text-decoration:line-through !important}.bx-normal-case{text-transform:none !important}select[multiple]{overflow:auto}#headerArea,#uhfSkipToMain,.uhf-footer{display:none}div[class*=NotFocusedDialog]{position:absolute !important;top:-9999px !important;left:-9999px !important;width:0 !important;height:0 !important}#game-stream video:not([src]){visibility:hidden}div[class*=SupportedInputsBadge]:not(:has(:nth-child(2))),div[class*=SupportedInputsBadge] svg:first-of-type{display:none}.bx-game-tile-wait-time{position:absolute;top:0;left:0;z-index:1;background:rgba(0,0,0,0.549);display:none;border-radius:0 0 4px 0;align-items:center;padding:4px 8px}a[class^=BaseItem-module__container]:focus .bx-game-tile-wait-time,button[class^=BaseItem-module__container]:focus .bx-game-tile-wait-time{display:flex}.bx-game-tile-wait-time svg{width:14px;height:16px;margin-right:2px}.bx-game-tile-wait-time span{display:inline-block;height:16px;line-height:16px;font-size:12px;font-weight:bold}.bx-button{--button-rgb:var(--bx-default-button-rgb);--button-hover-rgb:var(--bx-default-button-hover-rgb);--button-active-rgb:var(--bx-default-button-active-rgb);--button-disabled-rgb:var(--bx-default-button-disabled-rgb);background-color:rgb(var(--button-rgb));user-select:none;-webkit-user-select:none;color:#fff;font-family:var(--bx-title-font-semibold);font-size:14px;border:none;font-weight:400;height:var(--bx-button-height);border-radius:4px;padding:0 8px;text-transform:uppercase;cursor:pointer;overflow:hidden}.bx-button:not([disabled]):active{background-color:rgb(var(--button-active-rgb))}.bx-button:focus{outline:none !important}.bx-button:not([disabled]):not(:active):hover,.bx-button:not([disabled]):not(:active).bx-focusable:focus{background-color:rgb(var(--button-hover-rgb))}.bx-button:disabled{cursor:default;background-color:rgb(var(--button-disabled-rgb))}.bx-button.bx-ghost{background-color:transparent}.bx-button.bx-ghost:not([disabled]):not(:active):hover,.bx-button.bx-ghost:not([disabled]):not(:active).bx-focusable:focus{background-color:rgb(var(--button-hover-rgb))}.bx-button.bx-primary{--button-rgb:var(--bx-primary-button-rgb)}.bx-button.bx-primary:not([disabled]):active{--button-active-rgb:var(--bx-primary-button-active-rgb)}.bx-button.bx-primary:not([disabled]):not(:active):hover,.bx-button.bx-primary:not([disabled]):not(:active).bx-focusable:focus{--button-hover-rgb:var(--bx-primary-button-hover-rgb)}.bx-button.bx-primary:disabled{--button-disabled-rgb:var(--bx-primary-button-disabled-rgb)}.bx-button.bx-danger{--button-rgb:var(--bx-danger-button-rgb)}.bx-button.bx-danger:not([disabled]):active{--button-active-rgb:var(--bx-danger-button-active-rgb)}.bx-button.bx-danger:not([disabled]):not(:active):hover,.bx-button.bx-danger:not([disabled]):not(:active).bx-focusable:focus{--button-hover-rgb:var(--bx-danger-button-hover-rgb)}.bx-button.bx-danger:disabled{--button-disabled-rgb:var(--bx-danger-button-disabled-rgb)}.bx-button.bx-frosted{--button-alpha:.2;background-color:rgba(var(--button-rgb), var(--button-alpha));backdrop-filter:blur(4px) brightness(1.5)}.bx-button.bx-frosted:not([disabled]):not(:active):hover,.bx-button.bx-frosted:not([disabled]):not(:active).bx-focusable:focus{background-color:rgba(var(--button-hover-rgb), var(--button-alpha))}.bx-button.bx-drop-shadow{box-shadow:0 0 4px rgba(0,0,0,0.502)}.bx-button.bx-tall{height:calc(var(--bx-button-height) * 1.5) !important}.bx-button.bx-circular{border-radius:var(--bx-button-height);height:var(--bx-button-height)}.bx-button svg{display:inline-block;width:16px;height:var(--bx-button-height)}.bx-button span{display:inline-block;line-height:var(--bx-button-height);vertical-align:middle;color:#fff;overflow:hidden;white-space:nowrap}.bx-button span:not(:only-child){margin-left:10px}.screenshot-action-container{display:flex;gap:0}.bx-focusable{position:relative;overflow:visible}.bx-focusable::after{border:2px solid transparent;border-radius:10px}.bx-focusable:focus-visible::after{content:'';border-color:#fff;position:absolute;top:-6px;left:-6px;right:-6px;bottom:-6px}.bx-focusable.bx-circular::after{border-radius:var(--bx-button-height)}a.bx-button{display:inline-block}a.bx-button.bx-full-width{text-align:center}button.bx-inactive{pointer-events:none;opacity:.2;background:transparent !important}.bx-button-shortcut{max-width:max-content;margin:10px 0 0 0;overflow:hidden}@media (min-width:568px) and (max-height:480px){.bx-button-shortcut{margin:8px 0 0 10px}}.bx-header-remote-play-button{height:auto;margin-right:8px !important}.bx-header-remote-play-button svg{width:24px;height:24px}.bx-header-settings-button{line-height:30px;font-size:14px;text-transform:uppercase;position:relative}.bx-header-settings-button[data-update-available]::before{content:'🌟' !important;line-height:var(--bx-button-height);display:inline-block;margin-left:4px}.bx-dialog-overlay{position:fixed;inset:0;z-index:var(--bx-dialog-overlay-z-index);background:#000;opacity:50%}.bx-dialog{display:flex;flex-flow:column;max-height:90vh;position:fixed;top:50%;left:50%;margin-right:-50%;transform:translate(-50%,-50%);min-width:420px;padding:20px;border-radius:8px;z-index:var(--bx-dialog-z-index);background:#1a1b1e;color:#fff;font-weight:400;font-size:16px;font-family:var(--bx-normal-font);box-shadow:0 0 6px #000;user-select:none;-webkit-user-select:none}.bx-dialog *:focus{outline:none !important}.bx-dialog h2{display:flex;margin-bottom:12px}.bx-dialog h2 b{flex:1;color:#fff;display:block;font-family:var(--bx-title-font);font-size:26px;font-weight:400;line-height:var(--bx-button-height)}.bx-dialog.bx-binding-dialog h2 b{font-family:var(--bx-promptfont-font) !important}.bx-dialog > div{overflow:auto;padding:2px 0}.bx-dialog > button{padding:8px 32px;margin:10px auto 0;border:none;border-radius:4px;display:block;background-color:#2d3036;text-align:center;color:#fff;text-transform:uppercase;font-family:var(--bx-title-font);font-weight:400;line-height:18px;font-size:14px}@media (hover:hover){.bx-dialog > button:hover{background-color:#515863}}.bx-dialog > button:focus{background-color:#515863}@media screen and (max-width:450px){.bx-dialog{min-width:100%}}.bx-navigation-dialog{position:absolute;z-index:var(--bx-navigation-dialog-z-index)}.bx-navigation-dialog-overlay{position:fixed;background:rgba(11,11,11,0.89);top:0;left:0;right:0;bottom:0;z-index:var(--bx-navigation-dialog-overlay-z-index)}.bx-navigation-dialog-overlay[data-is-playing="true"]{background:transparent}.bx-settings-dialog{display:flex;position:fixed;top:0;right:0;bottom:0;opacity:.98;user-select:none;-webkit-user-select:none}.bx-settings-dialog .bx-focusable::after{border-radius:4px}.bx-settings-dialog .bx-focusable:focus::after{top:0;left:0;right:0;bottom:0}.bx-settings-dialog .bx-settings-reload-note{font-size:.8rem;display:block;padding:8px;font-style:italic;font-weight:normal;height:var(--bx-button-height)}.bx-settings-tabs-container{position:fixed;width:48px;max-height:100vh;display:flex;flex-direction:column}.bx-settings-tabs-container > div:last-of-type{display:flex;flex-direction:column;align-items:end}.bx-settings-tabs-container > div:last-of-type button{flex-shrink:0;border-top-right-radius:0;border-bottom-right-radius:0;margin-top:8px;height:unset;padding:8px 10px}.bx-settings-tabs-container > div:last-of-type button svg{width:16px;height:16px}.bx-settings-tabs{display:flex;flex-direction:column;border-radius:0 0 0 8px;box-shadow:0 0 6px #000;overflow:overlay;flex:1}.bx-settings-tabs svg{width:24px;height:24px;padding:10px;flex-shrink:0;box-sizing:content-box;background:#131313;cursor:pointer;border-left:4px solid #1e1e1e}.bx-settings-tabs svg.bx-active{background:#222;border-color:#008746}.bx-settings-tabs svg:not(.bx-active):hover{background:#2f2f2f;border-color:#484848}.bx-settings-tabs svg:focus{border-color:#fff;outline:none}.bx-settings-tabs svg[data-group=global][data-need-refresh=true]{background:var(--bx-danger-button-color) !important}.bx-settings-tabs svg[data-group=global][data-need-refresh=true]:hover{background:var(--bx-danger-button-hover-color) !important}.bx-settings-tab-contents{flex-direction:column;padding:10px;margin-left:48px;width:450px;max-width:calc(100vw - tabsWidth);background:#1a1b1e;color:#fff;font-weight:400;font-size:16px;font-family:var(--bx-title-font);text-align:center;box-shadow:0 0 6px #000;overflow:overlay;z-index:1}.bx-settings-tab-contents > div[data-tab-group=mkb]{display:flex;flex-direction:column;height:100%;overflow:hidden}.bx-settings-tab-contents > div[data-tab-group=shortcuts] > div[data-has-gamepad=true] > div:first-of-type{display:none}.bx-settings-tab-contents > div[data-tab-group=shortcuts] > div[data-has-gamepad=true] > div:last-of-type{display:block}.bx-settings-tab-contents > div[data-tab-group=shortcuts] > div[data-has-gamepad=false] > div:first-of-type{display:block}.bx-settings-tab-contents > div[data-tab-group=shortcuts] > div[data-has-gamepad=false] > div:last-of-type{display:none}.bx-settings-tab-contents > div[data-tab-group=shortcuts] .bx-shortcut-profile{width:100%;height:36px;display:block}.bx-settings-tab-contents > div[data-tab-group=shortcuts] .bx-shortcut-note{margin-top:10px;font-size:14px}.bx-settings-tab-contents > div[data-tab-group=shortcuts] .bx-shortcut-row{display:flex;margin-bottom:10px}.bx-settings-tab-contents > div[data-tab-group=shortcuts] .bx-shortcut-row label.bx-prompt{flex:1;font-size:26px;margin-bottom:0}.bx-settings-tab-contents > div[data-tab-group=shortcuts] .bx-shortcut-row .bx-shortcut-actions{flex:2;position:relative}.bx-settings-tab-contents > div[data-tab-group=shortcuts] .bx-shortcut-row .bx-shortcut-actions select{position:absolute;width:100%;height:100%;display:block}.bx-settings-tab-contents > div[data-tab-group=shortcuts] .bx-shortcut-row .bx-shortcut-actions select:last-of-type{opacity:0;z-index:calc(var(--bx-settings-z-index) + 1)}.bx-settings-tab-contents:focus,.bx-settings-tab-contents *:focus{outline:none !important}.bx-settings-tab-contents .bx-top-buttons{display:flex;flex-direction:column;gap:8px;margin-bottom:8px}.bx-settings-tab-contents .bx-top-buttons .bx-button{display:block}.bx-settings-tab-contents h2{margin:16px 0 8px 0;display:flex;align-items:center}.bx-settings-tab-contents h2:first-of-type{margin-top:0}.bx-settings-tab-contents h2 span{display:inline-block;font-size:20px;font-weight:bold;text-align:left;flex:1;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}@media (max-width:500px){.bx-settings-tab-contents{width:calc(100vw - 48px)}}.bx-settings-row{display:flex;gap:10px;border-bottom:1px solid #2c2c2e;padding:16px 8px;margin:0;border-left:2px solid transparent}.bx-settings-row:hover,.bx-settings-row:focus-within{background-color:#242424}.bx-settings-row:not(:has(> input[type=checkbox])){flex-wrap:wrap}.bx-settings-row input[type=checkbox]:focus,.bx-settings-row select:focus{filter:drop-shadow(1px 0 0 #fff) drop-shadow(-1px 0 0 #fff) drop-shadow(0 1px 0 #fff) drop-shadow(0 -1px 0 #fff)}.bx-settings-row:has(input:focus),.bx-settings-row:has(select:focus),.bx-settings-row:has(button:focus){border-left-color:#fff}.bx-settings-row > span.bx-settings-label{font-size:14px;display:block;text-align:left;align-self:center;margin-bottom:0 !important}.bx-settings-row > span.bx-settings-label + *{margin:0 0 0 auto}.bx-settings-row input{accent-color:var(--bx-primary-button-color)}.bx-settings-row input:focus{accent-color:var(--bx-danger-button-color)}.bx-settings-row select:disabled{-webkit-appearance:none;background:transparent;text-align-last:right;border:none;color:#fff}.bx-settings-row select option:disabled{display:none}.bx-settings-dialog-note{display:block;color:#afafb0;font-size:12px;font-weight:lighter;font-style:italic}.bx-settings-dialog-note:not(:has(a)){margin-top:4px}.bx-settings-dialog-note a{display:inline-block;padding:4px}.bx-settings-custom-user-agent{display:block;width:100%;padding:6px}.bx-donation-link{display:block;text-align:center;text-decoration:none;height:20px;line-height:20px;font-size:14px;margin-top:10px;color:#5dc21e}.bx-donation-link:hover{color:#6dd72b}.bx-donation-link:focus{text-decoration:underline}.bx-debug-info button{margin-top:10px}.bx-debug-info pre{margin-top:10px;cursor:copy;color:#fff;padding:8px;border:1px solid #2d2d2d;background:#212121;white-space:break-spaces;text-align:left}.bx-debug-info pre:hover{background:#272727}.bx-settings-app-version{margin-top:10px;text-align:center;color:#747474;font-size:12px}.bx-note-unsupported{display:block;font-size:12px;font-style:italic;font-weight:normal;color:#828282}.bx-toast{user-select:none;-webkit-user-select:none;position:fixed;left:50%;top:24px;transform:translate(-50%,0);background:#000;border-radius:16px;color:#fff;z-index:var(--bx-toast-z-index);font-family:var(--bx-normal-font);border:2px solid #fff;display:flex;align-items:center;opacity:0;overflow:clip;transition:opacity .2s ease-in}.bx-toast.bx-show{opacity:.85}.bx-toast.bx-hide{opacity:0;pointer-events:none}.bx-toast-msg{font-size:14px;display:inline-block;padding:12px 16px;white-space:pre}.bx-toast-status{font-weight:bold;font-size:14px;text-transform:uppercase;display:inline-block;background:#515863;padding:12px 16px;color:#fff;white-space:pre}.bx-wait-time-box{position:fixed;top:0;right:0;background-color:rgba(0,0,0,0.8);color:#fff;z-index:var(--bx-wait-time-box-z-index);padding:12px;border-radius:0 0 0 8px}.bx-wait-time-box label{display:block;text-transform:uppercase;text-align:right;font-size:12px;font-weight:bold;margin:0}.bx-wait-time-box span{display:block;font-family:var(--bx-monospaced-font);text-align:right;font-size:16px;margin-bottom:10px}.bx-wait-time-box span:last-of-type{margin-bottom:0}.bx-remote-play-popup{width:100%;max-width:1920px;margin:auto;position:relative;height:.1px;overflow:visible;z-index:var(--bx-remote-play-popup-z-index)}.bx-remote-play-container{position:absolute;right:10px;top:0;background:#1a1b1e;border-radius:10px;width:420px;max-width:calc(100vw - 20px);margin:0 0 0 auto;padding:20px;box-shadow:rgba(0,0,0,0.502) 0 0 12px 0}@media (min-width:480px) and (min-height:calc(480px + 1px)){.bx-remote-play-container{right:calc(env(safe-area-inset-right, 0px) + 32px)}}@media (min-width:768px) and (min-height:calc(480px + 1px)){.bx-remote-play-container{right:calc(env(safe-area-inset-right, 0px) + 48px)}}@media (min-width:1920px) and (min-height:calc(480px + 1px)){.bx-remote-play-container{right:calc(env(safe-area-inset-right, 0px) + 80px)}}.bx-remote-play-container > .bx-button{display:table;margin:0 0 0 auto}.bx-remote-play-settings{margin-bottom:12px;padding-bottom:12px;border-bottom:1px solid #2d2d2d}.bx-remote-play-settings > div{display:flex}.bx-remote-play-settings label{flex:1}.bx-remote-play-settings label p{margin:4px 0 0;padding:0;color:#888;font-size:12px}.bx-remote-play-settings span{font-weight:bold;font-size:18px;display:block;margin-bottom:8px;text-align:center}.bx-remote-play-resolution{display:block}.bx-remote-play-resolution input[type="radio"]{accent-color:var(--bx-primary-button-color);margin-right:6px}.bx-remote-play-resolution input[type="radio"]:focus{accent-color:var(--bx-primary-button-hover-color)}.bx-remote-play-device-wrapper{display:flex;margin-bottom:12px}.bx-remote-play-device-wrapper:last-child{margin-bottom:2px}.bx-remote-play-device-info{flex:1;padding:4px 0}.bx-remote-play-device-name{font-size:20px;font-weight:bold;display:inline-block;vertical-align:middle}.bx-remote-play-console-type{font-size:12px;background:#004c87;color:#fff;display:inline-block;border-radius:14px;padding:2px 10px;margin-left:8px;vertical-align:middle}.bx-remote-play-power-state{color:#888;font-size:14px}.bx-remote-play-connect-button{min-height:100%;margin:4px 0}.bx-select{display:flex;align-items:center;flex:0 1 auto}.bx-select select{display:none !important}.bx-select > div,.bx-select button.bx-select-value{min-width:110px;text-align:center;margin:0 8px;line-height:24px;vertical-align:middle;background:#fff;color:#000;border-radius:4px;padding:2px 8px;flex:1}.bx-select > div{display:inline-block}.bx-select > div input{display:inline-block;margin-right:8px}.bx-select > div label{margin-bottom:0;font-size:14px;width:100%}.bx-select > div label span{display:block;font-size:10px;font-weight:bold;text-align:left;line-height:initial}.bx-select button.bx-select-value{border:none;display:inline-flex;cursor:pointer;min-height:30px;font-size:.9rem;align-items:center}.bx-select button.bx-select-value span{flex:1;text-align:center;display:inline-block}.bx-select button.bx-select-value input{margin:0 4px;accent-color:var(--bx-primary-button-color)}.bx-select button.bx-select-value:hover input,.bx-select button.bx-select-value:focus input{accent-color:var(--bx-danger-button-color)}.bx-select button.bx-select-value:hover::after,.bx-select button.bx-select-value:focus::after{border-color:#4d4d4d !important}.bx-select button.bx-button{border:none;height:24px;width:24px;padding:0;line-height:24px;color:#fff;border-radius:4px;font-weight:bold;font-size:12px;font-family:var(--bx-monospaced-font);flex-shrink:0}.bx-select button.bx-button span{line-height:unset}div[class*=StreamMenu-module__menuContainer] > div[class*=Menu-module]{overflow:visible}.bx-stream-menu-button-on{fill:#000 !important;background-color:#2d2d2d !important;color:#000 !important}.bx-stream-refresh-button{top:calc(env(safe-area-inset-top, 0px) + 10px + 50px) !important}body[data-media-type=default] .bx-stream-refresh-button{left:calc(env(safe-area-inset-left, 0px) + 11px) !important}body[data-media-type=tv] .bx-stream-refresh-button{top:calc(var(--gds-focus-borderSize) + 80px) !important}.bx-stream-home-button{top:calc(env(safe-area-inset-top, 0px) + 10px + 50px * 2) !important}body[data-media-type=default] .bx-stream-home-button{left:calc(env(safe-area-inset-left, 0px) + 12px) !important}body[data-media-type=tv] .bx-stream-home-button{top:calc(var(--gds-focus-borderSize) + 80px * 2) !important}div[data-testid=media-container]{display:flex}div[data-testid=media-container].bx-taking-screenshot:before{animation:bx-anim-taking-screenshot .5s ease;content:' ';position:absolute;width:100%;height:100%;z-index:var(--bx-screenshot-animation-z-index)}#game-stream video{margin:auto;align-self:center;background:#000}#game-stream canvas{position:absolute;align-self:center;margin:auto;left:0;right:0}#gamepass-dialog-root div[class^=Guide-module__guide] .bx-button{overflow:visible;margin-bottom:12px}@-moz-keyframes bx-anim-taking-screenshot{0%{border:0 solid rgba(255,255,255,0.502)}50%{border:8px solid rgba(255,255,255,0.502)}100%{border:0 solid rgba(255,255,255,0.502)}}@-webkit-keyframes bx-anim-taking-screenshot{0%{border:0 solid rgba(255,255,255,0.502)}50%{border:8px solid rgba(255,255,255,0.502)}100%{border:0 solid rgba(255,255,255,0.502)}}@-o-keyframes bx-anim-taking-screenshot{0%{border:0 solid rgba(255,255,255,0.502)}50%{border:8px solid rgba(255,255,255,0.502)}100%{border:0 solid rgba(255,255,255,0.502)}}@keyframes bx-anim-taking-screenshot{0%{border:0 solid rgba(255,255,255,0.502)}50%{border:8px solid rgba(255,255,255,0.502)}100%{border:0 solid rgba(255,255,255,0.502)}}.bx-number-stepper{text-align:center}.bx-number-stepper span{display:inline-block;min-width:40px;font-family:var(--bx-monospaced-font);font-size:12px;margin:0 4px}.bx-number-stepper button{border:none;width:24px;height:24px;margin:0;line-height:24px;background-color:var(--bx-default-button-color);color:#fff;border-radius:4px;font-weight:bold;font-size:14px;font-family:var(--bx-monospaced-font)}@media (hover:hover){.bx-number-stepper button:hover{background-color:var(--bx-default-button-hover-color)}}.bx-number-stepper button:active{background-color:var(--bx-default-button-hover-color)}.bx-number-stepper button:disabled + span{font-family:var(--bx-title-font)}.bx-number-stepper input[type="range"]{display:block;margin:12px auto 2px;width:180px;color:#959595 !important}.bx-number-stepper input[type=range]:disabled,.bx-number-stepper button:disabled{display:none}.bx-number-stepper[data-disabled=true] input[type=range],.bx-number-stepper[data-disabled=true] button{display:none}#bx-game-bar{z-index:var(--bx-game-bar-z-index);position:fixed;bottom:0;width:40px;height:90px;overflow:visible;cursor:pointer}#bx-game-bar > svg{display:none;pointer-events:none;position:absolute;height:28px;margin-top:16px}@media (hover:hover){#bx-game-bar:hover > svg{display:block}}#bx-game-bar .bx-game-bar-container{opacity:0;position:absolute;display:flex;overflow:hidden;background:rgba(26,27,30,0.91);box-shadow:0 0 6px #1c1c1c;transition:opacity .1s ease-in}#bx-game-bar .bx-game-bar-container.bx-show{opacity:.9}#bx-game-bar .bx-game-bar-container.bx-show + svg{display:none !important}#bx-game-bar .bx-game-bar-container.bx-hide{opacity:0;pointer-events:none}#bx-game-bar .bx-game-bar-container button{width:60px;height:60px;border-radius:0}#bx-game-bar .bx-game-bar-container button svg{width:28px;height:28px;transition:transform .08s ease 0s}#bx-game-bar .bx-game-bar-container button:hover{border-radius:0}#bx-game-bar .bx-game-bar-container button:active svg{transform:scale(.75)}#bx-game-bar .bx-game-bar-container button.bx-activated{background-color:#fff}#bx-game-bar .bx-game-bar-container button.bx-activated svg{filter:invert(1)}#bx-game-bar .bx-game-bar-container div[data-enabled] button{display:none}#bx-game-bar .bx-game-bar-container div[data-enabled='true'] button:first-of-type{display:block}#bx-game-bar .bx-game-bar-container div[data-enabled='false'] button:last-of-type{display:block}#bx-game-bar[data-position="bottom-left"]{left:0;direction:ltr}#bx-game-bar[data-position="bottom-left"] .bx-game-bar-container{border-radius:0 10px 10px 0}#bx-game-bar[data-position="bottom-right"]{right:0;direction:rtl}#bx-game-bar[data-position="bottom-right"] .bx-game-bar-container{direction:ltr;border-radius:10px 0 0 10px}.bx-badges{margin-left:0;user-select:none;-webkit-user-select:none}.bx-badge{border:none;display:inline-block;line-height:24px;color:#fff;font-family:var(--bx-title-font-semibold);font-size:14px;font-weight:400;margin:0 8px 8px 0;box-shadow:0 0 6px #000;border-radius:4px}.bx-badge-name{background-color:#2d3036;border-radius:4px 0 0 4px}.bx-badge-name svg{width:16px;height:16px}.bx-badge-value{background-color:#808080;border-radius:0 4px 4px 0}.bx-badge-name,.bx-badge-value{display:inline-block;padding:0 8px;line-height:30px;vertical-align:bottom}.bx-badge-battery[data-charging=true] span:first-of-type::after{content:' ⚡️'}div[class^=StreamMenu-module__container] .bx-badges{position:absolute;max-width:500px}#gamepass-dialog-root .bx-badges{position:fixed;top:60px;left:460px;max-width:500px}@media (min-width:568px) and (max-height:480px){#gamepass-dialog-root .bx-badges{position:unset;top:unset;left:unset;margin:8px 0}}.bx-stats-bar{display:block;user-select:none;-webkit-user-select:none;position:fixed;top:0;background-color:#000;color:#fff;font-family:var(--bx-monospaced-font);font-size:.9rem;padding-left:8px;z-index:var(--bx-stats-bar-z-index);text-wrap:nowrap}.bx-stats-bar[data-stats*="[fps]"] > .bx-stat-fps,.bx-stats-bar[data-stats*="[ping]"] > .bx-stat-ping,.bx-stats-bar[data-stats*="[btr]"] > .bx-stat-btr,.bx-stats-bar[data-stats*="[dt]"] > .bx-stat-dt,.bx-stats-bar[data-stats*="[pl]"] > .bx-stat-pl,.bx-stats-bar[data-stats*="[fl]"] > .bx-stat-fl{display:inline-block}.bx-stats-bar[data-stats$="[fps]"] > .bx-stat-fps,.bx-stats-bar[data-stats$="[ping]"] > .bx-stat-ping,.bx-stats-bar[data-stats$="[btr]"] > .bx-stat-btr,.bx-stats-bar[data-stats$="[dt]"] > .bx-stat-dt,.bx-stats-bar[data-stats$="[pl]"] > .bx-stat-pl,.bx-stats-bar[data-stats$="[fl]"] > .bx-stat-fl{margin-right:0;border-right:none}.bx-stats-bar::before{display:none;content:'👀';vertical-align:middle;margin-right:8px}.bx-stats-bar[data-display=glancing]::before{display:inline-block}.bx-stats-bar[data-position=top-left]{left:0;border-radius:0 0 4px 0}.bx-stats-bar[data-position=top-right]{right:0;border-radius:0 0 0 4px}.bx-stats-bar[data-position=top-center]{transform:translate(-50%,0);left:50%;border-radius:0 0 4px 4px}.bx-stats-bar[data-transparent=true]{background:none;filter:drop-shadow(1px 0 0 rgba(0,0,0,0.941)) drop-shadow(-1px 0 0 rgba(0,0,0,0.941)) drop-shadow(0 1px 0 rgba(0,0,0,0.941)) drop-shadow(0 -1px 0 rgba(0,0,0,0.941))}.bx-stats-bar > div{display:none;margin-right:8px;border-right:1px solid #fff;padding-right:8px}.bx-stats-bar label{margin:0 8px 0 0;font-family:var(--bx-title-font);font-size:inherit;font-weight:bold;vertical-align:middle;cursor:help}.bx-stats-bar span{min-width:60px;display:inline-block;text-align:right;vertical-align:middle}.bx-stats-bar span[data-grade=good]{color:#6bffff}.bx-stats-bar span[data-grade=ok]{color:#fff16b}.bx-stats-bar span[data-grade=bad]{color:#ff5f5f}.bx-stats-bar span:first-of-type{min-width:22px}.bx-mkb-settings{display:flex;flex-direction:column;flex:1;padding-bottom:10px;overflow:hidden}.bx-mkb-settings select:disabled{-webkit-appearance:none;background:transparent;text-align-last:right;text-align:right;border:none;color:#fff}.bx-mkb-pointer-lock-msg{user-select:none;-webkit-user-select:none;position:fixed;left:50%;top:50%;transform:translateX(-50%) translateY(-50%);margin:auto;background:#151515;z-index:var(--bx-mkb-pointer-lock-msg-z-index);color:#fff;text-align:center;font-weight:400;font-family:"Segoe UI",Arial,Helvetica,sans-serif;font-size:1.3rem;padding:12px;border-radius:8px;align-items:center;box-shadow:0 0 6px #000;min-width:220px;opacity:.9}.bx-mkb-pointer-lock-msg:hover{opacity:1}.bx-mkb-pointer-lock-msg > div:first-of-type{display:flex;flex-direction:column;text-align:left}.bx-mkb-pointer-lock-msg p{margin:0}.bx-mkb-pointer-lock-msg p:first-child{font-size:22px;margin-bottom:4px;font-weight:bold}.bx-mkb-pointer-lock-msg p:last-child{font-size:12px;font-style:italic}.bx-mkb-pointer-lock-msg > div:last-of-type{margin-top:10px}.bx-mkb-pointer-lock-msg > div:last-of-type[data-type='native'] button:first-of-type{margin-bottom:8px}.bx-mkb-pointer-lock-msg > div:last-of-type[data-type='virtual'] div{display:flex;flex-flow:row;margin-top:8px}.bx-mkb-pointer-lock-msg > div:last-of-type[data-type='virtual'] div button{flex:1}.bx-mkb-pointer-lock-msg > div:last-of-type[data-type='virtual'] div button:first-of-type{margin-right:5px}.bx-mkb-pointer-lock-msg > div:last-of-type[data-type='virtual'] div button:last-of-type{margin-left:5px}.bx-mkb-preset-tools{display:flex;margin-bottom:12px}.bx-mkb-preset-tools select{flex:1}.bx-mkb-preset-tools button{margin-left:6px}.bx-mkb-settings-rows{flex:1;overflow:scroll}.bx-mkb-key-row{display:flex;margin-bottom:10px;align-items:center}.bx-mkb-key-row label{margin-bottom:0;font-family:var(--bx-promptfont-font);font-size:26px;text-align:center;width:26px;height:32px;line-height:32px}.bx-mkb-key-row button{flex:1;height:32px;line-height:32px;margin:0 0 0 10px;background:transparent;border:none;color:#fff;border-radius:0;border-left:1px solid #373737}.bx-mkb-key-row button:hover{background:transparent;cursor:default}.bx-mkb-settings.bx-editing .bx-mkb-key-row button{background:#393939;border-radius:4px;border:none}.bx-mkb-settings.bx-editing .bx-mkb-key-row button:hover{background:#333;cursor:pointer}.bx-mkb-action-buttons > div{text-align:right;display:none}.bx-mkb-action-buttons button{margin-left:8px}.bx-mkb-settings:not(.bx-editing) .bx-mkb-action-buttons > div:first-child{display:block}.bx-mkb-settings.bx-editing .bx-mkb-action-buttons > div:last-child{display:block}.bx-mkb-note{display:block;margin:16px 0 10px;font-size:12px}.bx-mkb-note:first-of-type{margin-top:0}`;
+ const PREF_HIDE_SECTIONS = getPref(PrefKey.UI_HIDE_SECTIONS), selectorToHide = [];
+ if (PREF_HIDE_SECTIONS.includes(UiSection.NEWS))
selectorToHide.push("#BodyContent > div[class*=CarouselRow-module]");
- if (PREF_HIDE_SECTIONS.includes("all-games"))
+ if (PREF_HIDE_SECTIONS.includes(UiSection.ALL_GAMES))
selectorToHide.push("#BodyContent div[class*=AllGamesRow-module__gridContainer]"), selectorToHide.push("#BodyContent div[class*=AllGamesRow-module__rowHeader]");
- if (PREF_HIDE_SECTIONS.includes("most-popular"))
+ if (PREF_HIDE_SECTIONS.includes(UiSection.MOST_POPULAR))
selectorToHide.push('#BodyContent div[class*=HomePage-module__bottomSpacing]:has(a[href="/play/gallery/popular"])');
- if (PREF_HIDE_SECTIONS.includes("touch"))
+ if (PREF_HIDE_SECTIONS.includes(UiSection.TOUCH))
selectorToHide.push('#BodyContent div[class*=HomePage-module__bottomSpacing]:has(a[href="/play/gallery/touch"])');
- if (getPref("block_social_features"))
+ if (getPref(PrefKey.BLOCK_SOCIAL_FEATURES))
selectorToHide.push("#gamepass-dialog-root div[class^=AchievementsPreview-module__container] + button[class*=HomeLandingPage-module__button]");
if (selectorToHide)
css += selectorToHide.join(",") + "{ display: none; }";
- if (getPref("reduce_animations"))
+ if (getPref(PrefKey.REDUCE_ANIMATIONS))
css += "div[class*=GameCard-module__gameTitleInnerWrapper],div[class*=GameCard-module__card],div[class*=ScrollArrows-module]{transition:none !important}";
- if (getPref("hide_dots_icon"))
+ if (getPref(PrefKey.HIDE_DOTS_ICON))
css += "div[class*=Grip-module__container]{visibility:hidden}@media (hover:hover){button[class*=GripHandle-module__container]:hover div[class*=Grip-module__container]{visibility:visible}}button[class*=GripHandle-module__container][aria-expanded=true] div[class*=Grip-module__container]{visibility:visible}button[class*=GripHandle-module__container][aria-expanded=false]{background-color:transparent !important}div[class*=StreamHUD-module__buttonsContainer]{padding:0 !important}";
- if (css += "div[class*=StreamMenu-module__menu]{min-width:100vw !important}", getPref("stream_simplify_menu"))
+ if (css += "div[class*=StreamMenu-module__menu]{min-width:100vw !important}", getPref(PrefKey.STREAM_SIMPLIFY_MENU))
css += "div[class*=Menu-module__scrollable]{--bxStreamMenuItemSize:80px;--streamMenuItemSize:calc(var(--bxStreamMenuItemSize) + 40px) !important}.bx-badges{top:calc(var(--streamMenuItemSize) - 20px)}body[data-media-type=tv] .bx-badges{top:calc(var(--streamMenuItemSize) - 10px) !important}button[class*=MenuItem-module__container]{min-width:auto !important;min-height:auto !important;width:var(--bxStreamMenuItemSize) !important;height:var(--bxStreamMenuItemSize) !important}div[class*=MenuItem-module__label]{display:none !important}svg[class*=MenuItem-module__icon]{width:36px;height:100% !important;padding:0 !important;margin:0 !important}";
else
css += "body[data-media-type=tv] .bx-badges{top:calc(var(--streamMenuItemSize) + 30px)}body:not([data-media-type=tv]) .bx-badges{top:calc(var(--streamMenuItemSize) + 20px)}body:not([data-media-type=tv]) button[class*=MenuItem-module__container]{min-width:auto !important;width:100px !important}body:not([data-media-type=tv]) button[class*=MenuItem-module__container]:nth-child(n+2){margin-left:10px !important}body:not([data-media-type=tv]) div[class*=MenuItem-module__label]{margin-left:8px !important;margin-right:8px !important}";
- if (getPref("ui_scrollbar_hide"))
+ if (getPref(PrefKey.UI_SCROLLBAR_HIDE))
css += "html{scrollbar-width:none}body::-webkit-scrollbar{display:none}";
const $style = CE("style", {}, css);
document.documentElement.appendChild($style);
@@ -6766,17 +7104,17 @@ function overridePreloadState() {
if (STATES.userAgent.capabilities.touch)
try {
const sigls = state.xcloud.sigls;
- if ("9c86f07a-f3e8-45ad-82a0-a1f759597059" in sigls) {
+ if (GamePassCloudGallery.TOUCH in sigls) {
let customList = TouchController.getCustomList();
- const allGames = sigls["29a81209-df6f-41fd-a528-2ae6b91f719c"].data.products;
- customList = customList.filter((id2) => allGames.includes(id2)), sigls["9c86f07a-f3e8-45ad-82a0-a1f759597059"]?.data.products.push(...customList);
+ const allGames = sigls[GamePassCloudGallery.ALL].data.products;
+ customList = customList.filter((id2) => allGames.includes(id2)), sigls[GamePassCloudGallery.TOUCH]?.data.products.push(...customList);
}
- if (BX_FLAGS.ForceNativeMkbTitles && "8fa264dd-124f-4af3-97e8-596fcdf4b486" in sigls)
- sigls["8fa264dd-124f-4af3-97e8-596fcdf4b486"]?.data.products.push(...BX_FLAGS.ForceNativeMkbTitles);
+ if (BX_FLAGS.ForceNativeMkbTitles && GamePassCloudGallery.NATIVE_MKB in sigls)
+ sigls[GamePassCloudGallery.NATIVE_MKB]?.data.products.push(...BX_FLAGS.ForceNativeMkbTitles);
} catch (e) {
BxLogger.error(LOG_TAG6, e);
}
- if (getPref("ui_home_context_menu_disabled"))
+ if (getPref(PrefKey.UI_HOME_CONTEXT_MENU_DISABLED))
try {
state.experiments.experimentationInfo.data.treatments.EnableHomeContextMenu = !1;
} catch (e) {
@@ -6844,9 +7182,9 @@ function patchSdpBitrate(sdp, video, audio) {
return lines.join("\r\n");
}
-var clarity_boost_default = "attribute vec2 position;\n\nvoid main() {\n gl_Position = vec4(position, 0, 1);\n}\n";
+var clarity_boost_default = "attribute vec2 position;\r\n\r\nvoid main() {\r\n gl_Position = vec4(position, 0, 1);\r\n}\r\n";
-var clarity_boost_default2 = "const int FILTER_UNSHARP_MASKING = 1;\nconst int FILTER_CAS = 2;\n\nprecision highp float;\nuniform sampler2D data;\nuniform vec2 iResolution;\n\nuniform int filterId;\nuniform float sharpenFactor;\nuniform float brightness;\nuniform float contrast;\nuniform float saturation;\n\nvec3 textureAt(sampler2D tex, vec2 coord) {\n return texture2D(tex, coord / iResolution.xy).rgb;\n}\n\nvec3 clarityBoost(sampler2D tex, vec2 coord)\n{\n // Load a collection of samples in a 3x3 neighorhood, where e is the current pixel.\n // a b c\n // d e f\n // g h i\n vec3 a = textureAt(tex, coord + vec2(-1, 1));\n vec3 b = textureAt(tex, coord + vec2(0, 1));\n vec3 c = textureAt(tex, coord + vec2(1, 1));\n\n vec3 d = textureAt(tex, coord + vec2(-1, 0));\n vec3 e = textureAt(tex, coord);\n vec3 f = textureAt(tex, coord + vec2(1, 0));\n\n vec3 g = textureAt(tex, coord + vec2(-1, -1));\n vec3 h = textureAt(tex, coord + vec2(0, -1));\n vec3 i = textureAt(tex, coord + vec2(1, -1));\n\n if (filterId == FILTER_CAS) {\n // Soft min and max.\n // a b c b\n // d e f * 0.5 + d e f * 0.5\n // g h i h\n // These are 2.0x bigger (factored out the extra multiply).\n vec3 minRgb = min(min(min(d, e), min(f, b)), h);\n vec3 minRgb2 = min(min(a, c), min(g, i));\n minRgb += min(minRgb, minRgb2);\n\n vec3 maxRgb = max(max(max(d, e), max(f, b)), h);\n vec3 maxRgb2 = max(max(a, c), max(g, i));\n maxRgb += max(maxRgb, maxRgb2);\n\n // Smooth minimum distance to signal limit divided by smooth max.\n vec3 reciprocalMaxRgb = 1.0 / maxRgb;\n vec3 amplifyRgb = clamp(min(minRgb, 2.0 - maxRgb) * reciprocalMaxRgb, 0.0, 1.0);\n\n // Shaping amount of sharpening.\n amplifyRgb = inversesqrt(amplifyRgb);\n\n float contrast = 0.8;\n float peak = -3.0 * contrast + 8.0;\n vec3 weightRgb = -(1.0 / (amplifyRgb * peak));\n\n vec3 reciprocalWeightRgb = 1.0 / (4.0 * weightRgb + 1.0);\n\n // 0 w 0\n // Filter shape: w 1 w\n // 0 w 0\n vec3 window = (b + d) + (f + h);\n vec3 outColor = clamp((window * weightRgb + e) * reciprocalWeightRgb, 0.0, 1.0);\n\n outColor = mix(e, outColor, sharpenFactor / 2.0);\n\n return outColor;\n } else if (filterId == FILTER_UNSHARP_MASKING) {\n vec3 gaussianBlur = (a * 1.0 + b * 2.0 + c * 1.0 +\n d * 2.0 + e * 4.0 + f * 2.0 +\n g * 1.0 + h * 2.0 + i * 1.0) / 16.0;\n\n // Return edge detection\n return e + (e - gaussianBlur) * sharpenFactor / 3.0;\n }\n\n return e;\n}\n\nvec3 adjustBrightness(vec3 color) {\n return (1.0 + brightness) * color;\n}\n\nvec3 adjustContrast(vec3 color) {\n return 0.5 + (1.0 + contrast) * (color - 0.5);\n}\n\nvec3 adjustSaturation(vec3 color) {\n const vec3 luminosityFactor = vec3(0.2126, 0.7152, 0.0722);\n vec3 grayscale = vec3(dot(color, luminosityFactor));\n\n return mix(grayscale, color, 1.0 + saturation);\n}\n\nvoid main() {\n vec3 color;\n\n if (sharpenFactor > 0.0) {\n color = clarityBoost(data, gl_FragCoord.xy);\n } else {\n color = textureAt(data, gl_FragCoord.xy);\n }\n\n if (saturation != 0.0) {\n color = adjustSaturation(color);\n }\n\n if (contrast != 0.0) {\n color = adjustContrast(color);\n }\n\n if (brightness != 0.0) {\n color = adjustBrightness(color);\n }\n\n gl_FragColor = vec4(color, 1.0);\n}\n";
+var clarity_boost_default2 = "const int FILTER_UNSHARP_MASKING = 1;\r\nconst int FILTER_CAS = 2;\r\n\r\nprecision highp float;\r\nuniform sampler2D data;\r\nuniform vec2 iResolution;\r\n\r\nuniform int filterId;\r\nuniform float sharpenFactor;\r\nuniform float brightness;\r\nuniform float contrast;\r\nuniform float saturation;\r\n\r\nvec3 textureAt(sampler2D tex, vec2 coord) {\r\n return texture2D(tex, coord / iResolution.xy).rgb;\r\n}\r\n\r\nvec3 clarityBoost(sampler2D tex, vec2 coord)\r\n{\r\n // Load a collection of samples in a 3x3 neighorhood, where e is the current pixel.\r\n // a b c\r\n // d e f\r\n // g h i\r\n vec3 a = textureAt(tex, coord + vec2(-1, 1));\r\n vec3 b = textureAt(tex, coord + vec2(0, 1));\r\n vec3 c = textureAt(tex, coord + vec2(1, 1));\r\n\r\n vec3 d = textureAt(tex, coord + vec2(-1, 0));\r\n vec3 e = textureAt(tex, coord);\r\n vec3 f = textureAt(tex, coord + vec2(1, 0));\r\n\r\n vec3 g = textureAt(tex, coord + vec2(-1, -1));\r\n vec3 h = textureAt(tex, coord + vec2(0, -1));\r\n vec3 i = textureAt(tex, coord + vec2(1, -1));\r\n\r\n if (filterId == FILTER_CAS) {\r\n // Soft min and max.\r\n // a b c b\r\n // d e f * 0.5 + d e f * 0.5\r\n // g h i h\r\n // These are 2.0x bigger (factored out the extra multiply).\r\n vec3 minRgb = min(min(min(d, e), min(f, b)), h);\r\n vec3 minRgb2 = min(min(a, c), min(g, i));\r\n minRgb += min(minRgb, minRgb2);\r\n\r\n vec3 maxRgb = max(max(max(d, e), max(f, b)), h);\r\n vec3 maxRgb2 = max(max(a, c), max(g, i));\r\n maxRgb += max(maxRgb, maxRgb2);\r\n\r\n // Smooth minimum distance to signal limit divided by smooth max.\r\n vec3 reciprocalMaxRgb = 1.0 / maxRgb;\r\n vec3 amplifyRgb = clamp(min(minRgb, 2.0 - maxRgb) * reciprocalMaxRgb, 0.0, 1.0);\r\n\r\n // Shaping amount of sharpening.\r\n amplifyRgb = inversesqrt(amplifyRgb);\r\n\r\n float contrast = 0.8;\r\n float peak = -3.0 * contrast + 8.0;\r\n vec3 weightRgb = -(1.0 / (amplifyRgb * peak));\r\n\r\n vec3 reciprocalWeightRgb = 1.0 / (4.0 * weightRgb + 1.0);\r\n\r\n // 0 w 0\r\n // Filter shape: w 1 w\r\n // 0 w 0\r\n vec3 window = (b + d) + (f + h);\r\n vec3 outColor = clamp((window * weightRgb + e) * reciprocalWeightRgb, 0.0, 1.0);\r\n\r\n outColor = mix(e, outColor, sharpenFactor / 2.0);\r\n\r\n return outColor;\r\n } else if (filterId == FILTER_UNSHARP_MASKING) {\r\n vec3 gaussianBlur = (a * 1.0 + b * 2.0 + c * 1.0 +\r\n d * 2.0 + e * 4.0 + f * 2.0 +\r\n g * 1.0 + h * 2.0 + i * 1.0) / 16.0;\r\n\r\n // Return edge detection\r\n return e + (e - gaussianBlur) * sharpenFactor / 3.0;\r\n }\r\n\r\n return e;\r\n}\r\n\r\nvec3 adjustBrightness(vec3 color) {\r\n return (1.0 + brightness) * color;\r\n}\r\n\r\nvec3 adjustContrast(vec3 color) {\r\n return 0.5 + (1.0 + contrast) * (color - 0.5);\r\n}\r\n\r\nvec3 adjustSaturation(vec3 color) {\r\n const vec3 luminosityFactor = vec3(0.2126, 0.7152, 0.0722);\r\n vec3 grayscale = vec3(dot(color, luminosityFactor));\r\n\r\n return mix(grayscale, color, 1.0 + saturation);\r\n}\r\n\r\nvoid main() {\r\n vec3 color;\r\n\r\n if (sharpenFactor > 0.0) {\r\n color = clarityBoost(data, gl_FragCoord.xy);\r\n } else {\r\n color = textureAt(data, gl_FragCoord.xy);\r\n }\r\n\r\n if (saturation != 0.0) {\r\n color = adjustSaturation(color);\r\n }\r\n\r\n if (contrast != 0.0) {\r\n color = adjustContrast(color);\r\n }\r\n\r\n if (brightness != 0.0) {\r\n color = adjustBrightness(color);\r\n }\r\n\r\n gl_FragColor = vec4(color, 1.0);\r\n}\r\n";
var LOG_TAG7 = "WebGL2Player";
@@ -6913,12 +7251,12 @@ class WebGL2Player {
}, this.#animFrameId = requestAnimationFrame(animate);
}
#setupShaders() {
- BxLogger.info(LOG_TAG7, "Setting up", getPref("video_power_preference"));
+ BxLogger.info(LOG_TAG7, "Setting up", getPref(PrefKey.VIDEO_POWER_PREFERENCE));
const gl = this.#$canvas.getContext("webgl2", {
isBx: !0,
antialias: !0,
alpha: !1,
- powerPreference: getPref("video_power_preference")
+ powerPreference: getPref(PrefKey.VIDEO_POWER_PREFERENCE)
});
this.#gl = gl, gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferWidth);
const vShader = gl.createShader(gl.VERTEX_SHADER);
@@ -6983,7 +7321,7 @@ class WebGL2Player {
class StreamPlayer {
#$video;
- #playerType = "default";
+ #playerType = StreamPlayerType.VIDEO;
#options = {};
#webGL2Player = null;
#$videoCss = null;
@@ -7014,7 +7352,7 @@ class StreamPlayer {
}
#getVideoPlayerFilterStyle() {
const filters = [], sharpness = this.#options.sharpness || 0;
- if (this.#options.processing === "usm" && sharpness != 0) {
+ if (this.#options.processing === StreamVideoProcessing.USM && sharpness != 0) {
const matrix = `0 -1 0 -1 ${(7 - (sharpness / 2 - 1) * 0.5).toFixed(1)} -1 0 -1 0`;
this.#$usmMatrix?.setAttributeNS(null, "kernelMatrix", matrix), filters.push("url(#bx-filter-usm)");
}
@@ -7030,9 +7368,9 @@ class StreamPlayer {
return filters.join(" ");
}
#resizePlayer() {
- const PREF_RATIO = getPref("video_ratio"), $video = this.#$video, isNativeTouchGame = STATES.currentStream.titleInfo?.details.hasNativeTouchSupport;
+ const PREF_RATIO = getPref(PrefKey.VIDEO_RATIO), $video = this.#$video, isNativeTouchGame = STATES.currentStream.titleInfo?.details.hasNativeTouchSupport;
let $webGL2Canvas;
- if (this.#playerType == "webgl2")
+ if (this.#playerType == StreamPlayerType.WEBGL2)
$webGL2Canvas = this.#webGL2Player?.getCanvas();
let targetWidth, targetHeight, targetObjectFit;
if (PREF_RATIO.includes(":")) {
@@ -7048,12 +7386,12 @@ class StreamPlayer {
targetWidth = "100%", targetHeight = "100%", targetObjectFit = PREF_RATIO, $video.dataset.width = window.innerWidth.toString(), $video.dataset.height = window.innerHeight.toString();
if ($video.style.width = targetWidth, $video.style.height = targetHeight, $video.style.objectFit = targetObjectFit, $webGL2Canvas)
$webGL2Canvas.style.width = targetWidth, $webGL2Canvas.style.height = targetHeight, $webGL2Canvas.style.objectFit = targetObjectFit;
- if (isNativeTouchGame && this.#playerType == "webgl2")
+ if (isNativeTouchGame && this.#playerType == StreamPlayerType.WEBGL2)
window.BX_EXPOSED.streamSession.updateDimensions();
}
setPlayerType(type, refreshPlayer = !1) {
if (this.#playerType !== type)
- if (type === "webgl2") {
+ if (type === StreamPlayerType.WEBGL2) {
if (!this.#webGL2Player)
this.#webGL2Player = new WebGL2Player(this.#$video);
else
@@ -7072,7 +7410,7 @@ class StreamPlayer {
getPlayerElement(playerType) {
if (typeof playerType === "undefined")
playerType = this.#playerType;
- if (playerType === "webgl2")
+ if (playerType === StreamPlayerType.WEBGL2)
return this.#webGL2Player?.getCanvas();
return this.#$video;
}
@@ -7080,9 +7418,9 @@ class StreamPlayer {
return this.#webGL2Player;
}
refreshPlayer() {
- if (this.#playerType === "webgl2") {
+ if (this.#playerType === StreamPlayerType.WEBGL2) {
const options = this.#options, webGL2Player = this.#webGL2Player;
- if (options.processing === "usm")
+ if (options.processing === StreamVideoProcessing.USM)
webGL2Player.setFilter(1);
else
webGL2Player.setFilter(2);
@@ -7091,7 +7429,7 @@ class StreamPlayer {
let filters = this.#getVideoPlayerFilterStyle(), videoCss = "";
if (filters)
videoCss += `filter: ${filters} !important;`;
- if (getPref("screenshot_apply_filters"))
+ if (getPref(PrefKey.SCREENSHOT_APPLY_FILTERS))
Screenshot.updateCanvasFilters(filters);
let css = "";
if (videoCss)
@@ -7101,7 +7439,7 @@ class StreamPlayer {
this.#resizePlayer();
}
reloadPlayer() {
- this.#cleanUpWebGL2Player(), this.#playerType = "default", this.setPlayerType("webgl2", !1);
+ this.#cleanUpWebGL2Player(), this.#playerType = StreamPlayerType.VIDEO, this.setPlayerType(StreamPlayerType.WEBGL2, !1);
}
#cleanUpWebGL2Player() {
this.#webGL2Player?.destroy(), this.#webGL2Player = null;
@@ -7112,17 +7450,17 @@ class StreamPlayer {
}
function patchVideoApi() {
- const PREF_SKIP_SPLASH_VIDEO = getPref("skip_splash_video"), showFunc = function() {
+ const PREF_SKIP_SPLASH_VIDEO = getPref(PrefKey.SKIP_SPLASH_VIDEO), showFunc = function() {
if (this.style.visibility = "visible", !this.videoWidth)
return;
const playerOptions = {
- processing: getPref("video_processing"),
- sharpness: getPref("video_sharpness"),
- saturation: getPref("video_saturation"),
- contrast: getPref("video_contrast"),
- brightness: getPref("video_brightness")
+ processing: getPref(PrefKey.VIDEO_PROCESSING),
+ sharpness: getPref(PrefKey.VIDEO_SHARPNESS),
+ saturation: getPref(PrefKey.VIDEO_SATURATION),
+ contrast: getPref(PrefKey.VIDEO_CONTRAST),
+ brightness: getPref(PrefKey.VIDEO_BRIGHTNESS)
};
- STATES.currentStream.streamPlayer = new StreamPlayer(this, getPref("video_player_type"), playerOptions), BxEvent.dispatch(window, BxEvent.STREAM_PLAYING, {
+ STATES.currentStream.streamPlayer = new StreamPlayer(this, getPref(PrefKey.VIDEO_PLAYER_TYPE), playerOptions), BxEvent.dispatch(window, BxEvent.STREAM_PLAYING, {
$video: this
});
}, nativePlay = HTMLMediaElement.prototype.play;
@@ -7140,7 +7478,7 @@ function patchVideoApi() {
};
}
function patchRtcCodecs() {
- if (getPref("stream_codec_profile") === "default")
+ if (getPref(PrefKey.STREAM_CODEC_PROFILE) === "default")
return;
if (typeof RTCRtpTransceiver === "undefined" || !("setCodecPreferences" in RTCRtpTransceiver.prototype))
return !1;
@@ -7153,7 +7491,7 @@ function patchRtcPeerConnection() {
dataChannel
}), dataChannel;
};
- const maxVideoBitrate = getPref("bitrate_video_max"), codec = getPref("stream_codec_profile");
+ const maxVideoBitrate = getPref(PrefKey.BITRATE_VIDEO_MAX), codec = getPref(PrefKey.STREAM_CODEC_PROFILE);
if (codec !== "default" || maxVideoBitrate > 0) {
const nativeSetLocalDescription = RTCPeerConnection.prototype.setLocalDescription;
RTCPeerConnection.prototype.setLocalDescription = function(description) {
@@ -7184,7 +7522,7 @@ function patchAudioContext() {
const ctx = new OrgAudioContext(options);
return BxLogger.info("patchAudioContext", ctx, options), ctx.createGain = function() {
const gainNode = nativeCreateGain.apply(this);
- return gainNode.gain.value = getPref("audio_volume") / 100, STATES.currentStream.audioGainNode = gainNode, gainNode;
+ return gainNode.gain.value = getPref(PrefKey.AUDIO_VOLUME) / 100, STATES.currentStream.audioGainNode = gainNode, gainNode;
}, STATES.currentStream.audioContext = ctx, ctx;
};
}
@@ -7265,7 +7603,7 @@ function patchPointerLockApi() {
};
}
-function cloneStreamHudButton($orgButton, label, svgIcon) {
+var cloneStreamHudButton = function($orgButton, label, svgIcon) {
const $container = $orgButton.cloneNode(!0);
let timeout;
const onTransitionStart = (e) => {
@@ -7286,11 +7624,10 @@ function cloneStreamHudButton($orgButton, label, svgIcon) {
$button.setAttribute("title", label);
const $orgSvg = $button.querySelector("svg"), $svg = createSvgIcon(svgIcon);
return $svg.style.fill = "none", $svg.setAttribute("class", $orgSvg.getAttribute("class") || ""), $svg.ariaHidden = "true", $orgSvg.replaceWith($svg), $container;
-}
-function cloneCloseButton($$btnOrg, icon, className, onChange) {
+}, cloneCloseButton = function($$btnOrg, icon, className, onChange) {
const $btn = $$btnOrg.cloneNode(!0), $svg = createSvgIcon(icon);
return $svg.setAttribute("class", $btn.firstElementChild.getAttribute("class") || ""), $svg.style.fill = "none", $btn.classList.add(className), $btn.removeChild($btn.firstElementChild), $btn.appendChild($svg), $btn.addEventListener("click", onChange), $btn;
-}
+};
function injectStreamMenuButtons() {
const $screen = document.querySelector("#PageContent section[class*=PureScreens]");
if (!$screen)
@@ -7438,7 +7775,7 @@ class MicrophoneAction extends BaseGameBarAction {
onClick
});
this.$content = CE("div", {}, $btnDefault, $btnMuted), this.reset(), window.addEventListener(BxEvent.MICROPHONE_STATE_CHANGED, (e) => {
- const enabled = e.microphoneState === "Enabled";
+ const enabled = e.microphoneState === MicrophoneState.ENABLED;
this.$content.setAttribute("data-enabled", enabled.toString()), this.$content.classList.remove("bx-gone");
});
}
@@ -7464,10 +7801,10 @@ class GameBar {
actions = [];
constructor() {
let $container;
- const position = getPref("game_bar_position"), $gameBar = CE("div", { id: "bx-game-bar", class: "bx-gone", "data-position": position }, $container = CE("div", { class: "bx-game-bar-container bx-offscreen" }), createSvgIcon(position === "bottom-left" ? BxIcon.CARET_RIGHT : BxIcon.CARET_LEFT));
+ const position = getPref(PrefKey.GAME_BAR_POSITION), $gameBar = CE("div", { id: "bx-game-bar", class: "bx-gone", "data-position": position }, $container = CE("div", { class: "bx-game-bar-container bx-offscreen" }), createSvgIcon(position === "bottom-left" ? BxIcon.CARET_RIGHT : BxIcon.CARET_LEFT));
if (this.actions = [
new ScreenshotAction,
- ...STATES.userAgent.capabilities.touch && getPref("stream_touch_controller") !== "off" ? [new TouchControlAction] : [],
+ ...STATES.userAgent.capabilities.touch && getPref(PrefKey.STREAM_TOUCH_CONTROLLER) !== "off" ? [new TouchControlAction] : [],
new MicrophoneAction
], position === "bottom-right")
this.actions.reverse();
@@ -7481,7 +7818,7 @@ class GameBar {
const classList = $container.classList;
if (classList.contains("bx-hide"))
classList.remove("bx-offscreen", "bx-hide"), classList.add("bx-offscreen");
- }), document.documentElement.appendChild($gameBar), this.$gameBar = $gameBar, this.$container = $container, getPref("game_bar_position") !== "off" && window.addEventListener(BxEvent.XCLOUD_POLLING_MODE_CHANGED, ((e) => {
+ }), document.documentElement.appendChild($gameBar), this.$gameBar = $gameBar, this.$container = $container, getPref(PrefKey.GAME_BAR_POSITION) !== "off" && window.addEventListener(BxEvent.XCLOUD_POLLING_MODE_CHANGED, ((e) => {
if (!STATES.isPlaying) {
this.disable();
return;
@@ -7519,6 +7856,11 @@ class GameBar {
}
}
+var GuideMenuTab;
+(function(GuideMenuTab2) {
+ GuideMenuTab2["HOME"] = "home";
+})(GuideMenuTab || (GuideMenuTab = {}));
+
class GuideMenu {
static #BUTTONS = {
scriptSettings: createButton({
@@ -7590,7 +7932,7 @@ class GuideMenu {
$btnXcloudHome && ($btnXcloudHome.style.display = "none");
}
static async#onShown(e) {
- if (e.where === "home") {
+ if (e.where === GuideMenuTab.HOME) {
const $root = document.querySelector("#gamepass-dialog-root div[role=dialog] div[role=tabpanel] div[class*=HomeLandingPage]");
if ($root)
if (STATES.isPlaying)
@@ -7737,12 +8079,11 @@ class ProductDetailsPage {
}
}
-function unload() {
+var unload = function() {
if (!STATES.isPlaying)
return;
EmulatedMkbHandler.getInstance().destroy(), NativeMkbHandler.getInstance().destroy(), STATES.currentStream.streamPlayer?.destroy(), STATES.isPlaying = !1, STATES.currentStream = {}, window.BX_EXPOSED.shouldShowSensorControls = !1, window.BX_EXPOSED.stopTakRendering = !1, NavigationDialogManager.getInstance().hide(), StreamStats.getInstance().onStoppedPlaying(), MouseCursorHider.stop(), TouchController.reset(), GameBar.getInstance().disable();
-}
-function observeRootDialog($root) {
+}, observeRootDialog = function($root) {
let currentShown = !1;
new MutationObserver((mutationList) => {
for (let mutation of mutationList) {
@@ -7759,7 +8100,7 @@ function observeRootDialog($root) {
for (index = 0;$elm = $elm?.previousElementSibling; index++)
;
if (index === 0)
- BxEvent.dispatch(window, BxEvent.XCLOUD_GUIDE_MENU_SHOWN, { where: "home" });
+ BxEvent.dispatch(window, BxEvent.XCLOUD_GUIDE_MENU_SHOWN, { where: GuideMenuTab.HOME });
}
}
}
@@ -7770,8 +8111,7 @@ function observeRootDialog($root) {
currentShown = shown, BxEvent.dispatch(window, shown ? BxEvent.XCLOUD_DIALOG_SHOWN : BxEvent.XCLOUD_DIALOG_DISMISSED);
}
}).observe($root, { subtree: !0, childList: !0 });
-}
-function waitForRootDialog() {
+}, waitForRootDialog = function() {
const observer = new MutationObserver((mutationList) => {
for (let mutation of mutationList) {
if (mutation.type !== "childList")
@@ -7784,20 +8124,19 @@ function waitForRootDialog() {
}
});
observer.observe(document.documentElement, { subtree: !0, childList: !0 });
-}
-function main() {
- if (waitForRootDialog(), patchRtcPeerConnection(), patchRtcCodecs(), interceptHttpRequests(), patchVideoApi(), patchCanvasContext(), AppInterface && patchPointerLockApi(), getPref("audio_enable_volume_control") && patchAudioContext(), getPref("block_tracking"))
+}, main = function() {
+ if (waitForRootDialog(), patchRtcPeerConnection(), patchRtcCodecs(), interceptHttpRequests(), patchVideoApi(), patchCanvasContext(), AppInterface && patchPointerLockApi(), getPref(PrefKey.AUDIO_ENABLE_VOLUME_CONTROL) && patchAudioContext(), getPref(PrefKey.BLOCK_TRACKING))
patchMeControl(), disableAdobeAudienceManager();
- if (STATES.userAgent.capabilities.touch && TouchController.updateCustomList(), overridePreloadState(), VibrationManager.initialSetup(), BX_FLAGS.CheckForUpdate && checkForUpdate(), addCss(), Toast.setup(), getPref("game_bar_position") !== "off" && GameBar.getInstance(), Screenshot.setup(), GuideMenu.observe(), StreamBadges.setupEvents(), StreamStats.setupEvents(), EmulatedMkbHandler.setupEvents(), Patcher.init(), disablePwa(), getPref("controller_show_connection_status"))
+ if (STATES.userAgent.capabilities.touch && TouchController.updateCustomList(), overridePreloadState(), VibrationManager.initialSetup(), BX_FLAGS.CheckForUpdate && checkForUpdate(), addCss(), Toast.setup(), getPref(PrefKey.GAME_BAR_POSITION) !== "off" && GameBar.getInstance(), Screenshot.setup(), GuideMenu.observe(), StreamBadges.setupEvents(), StreamStats.setupEvents(), EmulatedMkbHandler.setupEvents(), Patcher.init(), disablePwa(), getPref(PrefKey.CONTROLLER_SHOW_CONNECTION_STATUS))
window.addEventListener("gamepadconnected", (e) => showGamepadToast(e.gamepad)), window.addEventListener("gamepaddisconnected", (e) => showGamepadToast(e.gamepad));
- if (getPref("xhome_enabled"))
+ if (getPref(PrefKey.REMOTE_PLAY_ENABLED))
RemotePlay.detect();
- if (getPref("stream_touch_controller") === "all")
+ if (getPref(PrefKey.STREAM_TOUCH_CONTROLLER) === "all")
TouchController.setup();
- if (getPref("mkb_enabled") && AppInterface)
+ if (getPref(PrefKey.MKB_ENABLED) && AppInterface)
STATES.pointerServerPort = AppInterface.startPointerServer() || 9269, BxLogger.info("startPointerServer", "Port", STATES.pointerServerPort.toString());
- getPref("ui_game_card_show_wait_time") && GameTile.setup();
-}
+ getPref(PrefKey.UI_GAME_CARD_SHOW_WAIT_TIME) && GameTile.setup();
+};
if (window.location.pathname.includes("/auth/msa")) {
const nativePushState = window.history.pushState;
throw window.history.pushState = function(...args) {
@@ -7839,10 +8178,10 @@ document.addEventListener("readystatechange", (e) => {
if (document.readyState !== "interactive")
return;
if (STATES.isSignedIn = window.xbcUser?.isSignedIn, STATES.isSignedIn)
- getPref("xhome_enabled") && RemotePlay.preload();
+ getPref(PrefKey.REMOTE_PLAY_ENABLED) && RemotePlay.preload();
else
HeaderSection.watchHeader();
- if (getPref("ui_hide_sections").includes("friends")) {
+ if (getPref(PrefKey.UI_HIDE_SECTIONS).includes(UiSection.FRIENDS)) {
const $parent = document.querySelector("div[class*=PlayWithFriendsSkeleton]")?.closest("div[class*=HomePage-module]");
$parent && ($parent.style.display = "none");
}
@@ -7867,13 +8206,13 @@ window.addEventListener(BxEvent.STREAM_LOADING, (e) => {
} else
STATES.currentStream.titleId = "remote-play", STATES.currentStream.productId = "";
});
-getPref("ui_loading_screen_game_art") && window.addEventListener(BxEvent.TITLE_INFO_READY, LoadingScreen.setup);
+getPref(PrefKey.UI_LOADING_SCREEN_GAME_ART) && window.addEventListener(BxEvent.TITLE_INFO_READY, LoadingScreen.setup);
window.addEventListener(BxEvent.STREAM_STARTING, (e) => {
- if (LoadingScreen.hide(), !getPref("mkb_enabled") && getPref("mkb_hide_idle_cursor"))
+ if (LoadingScreen.hide(), !getPref(PrefKey.MKB_ENABLED) && getPref(PrefKey.MKB_HIDE_IDLE_CURSOR))
MouseCursorHider.start(), MouseCursorHider.hide();
});
window.addEventListener(BxEvent.STREAM_PLAYING, (e) => {
- if (STATES.isPlaying = !0, injectStreamMenuButtons(), getPref("game_bar_position") !== "off") {
+ if (STATES.isPlaying = !0, injectStreamMenuButtons(), getPref(PrefKey.GAME_BAR_POSITION) !== "off") {
const gameBar = GameBar.getInstance();
gameBar.reset(), gameBar.enable(), gameBar.showBar();
}
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 00000000..ba3a92c5
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,1407 @@
+{
+ "name": "better-xcloud",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "better-xcloud",
+ "bin": {
+ "build": "build.ts"
+ },
+ "devDependencies": {
+ "@types/bun": "^1.1.6",
+ "@types/node": "^20.14.12",
+ "@types/stylus": "^0.48.42",
+ "eslint": "^9.8.0",
+ "eslint-plugin-compat": "^6.0.0",
+ "stylus": "^0.63.0"
+ },
+ "peerDependencies": {
+ "typescript": "^5.5.2"
+ }
+ },
+ "node_modules/@adobe/css-tools": {
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.3.tgz",
+ "integrity": "sha512-rE0Pygv0sEZ4vBWHlAgJLGDU7Pm8xoO6p3wsEceb7GYAjScrOHpEo8KK/eVkAcnSM+slAEtXjA2JpdjLp4fJQQ==",
+ "dev": true
+ },
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
+ "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",
+ "dev": true,
+ "dependencies": {
+ "eslint-visitor-keys": "^3.3.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.11.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz",
+ "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==",
+ "dev": true,
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/config-array": {
+ "version": "0.17.1",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.17.1.tgz",
+ "integrity": "sha512-BlYOpej8AQ8Ev9xVqroV7a02JK3SkBAaN9GfMMH9W6Ch8FlQlkjGw4Ir7+FgYwfirivAf4t+GtzuAxqfukmISA==",
+ "dev": true,
+ "dependencies": {
+ "@eslint/object-schema": "^2.1.4",
+ "debug": "^4.3.1",
+ "minimatch": "^3.1.2"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz",
+ "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==",
+ "dev": true,
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^10.0.1",
+ "globals": "^14.0.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.0",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/js": {
+ "version": "9.8.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.8.0.tgz",
+ "integrity": "sha512-MfluB7EUfxXtv3i/++oh89uzAr4PDI4nn201hsp+qaXqsjAWzinlZEHEfPgAX4doIlKvPG/i0A9dpKxOLII8yA==",
+ "dev": true,
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/object-schema": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz",
+ "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==",
+ "dev": true,
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/retry": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.0.tgz",
+ "integrity": "sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==",
+ "dev": true,
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@mdn/browser-compat-data": {
+ "version": "5.5.42",
+ "resolved": "https://registry.npmjs.org/@mdn/browser-compat-data/-/browser-compat-data-5.5.42.tgz",
+ "integrity": "sha512-qhHVgb2dxaFNT00Z1upHaDCstUEjjrgtIkrk4tr+YnDSGbTIKncbdydIpSed+RCXz0f6nb4UDD4eKEWokNom6g==",
+ "dev": true
+ },
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "dev": true,
+ "dependencies": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "dev": true,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "dev": true,
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@types/bun": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/@types/bun/-/bun-1.1.6.tgz",
+ "integrity": "sha512-uJgKjTdX0GkWEHZzQzFsJkWp5+43ZS7HC8sZPFnOwnSo1AsNl2q9o2bFeS23disNDqbggEgyFkKCHl/w8iZsMA==",
+ "dev": true,
+ "dependencies": {
+ "bun-types": "1.1.17"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "20.14.13",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.13.tgz",
+ "integrity": "sha512-+bHoGiZb8UiQ0+WEtmph2IWQCjIqg8MDZMAV+ppRRhUZnquF5mQkP/9vpSwJClEiSM/C7fZZExPzfU0vJTyp8w==",
+ "dev": true,
+ "dependencies": {
+ "undici-types": "~5.26.4"
+ }
+ },
+ "node_modules/@types/stylus": {
+ "version": "0.48.42",
+ "resolved": "https://registry.npmjs.org/@types/stylus/-/stylus-0.48.42.tgz",
+ "integrity": "sha512-CPGlr5teL4sqdap+EOowMifLuNGeIoLwc0VQ7u/BPxo+ocqiNa5jeVt0H0IVBblEh6ZwX1sGpIQIFnSSr8NBQA==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/ws": {
+ "version": "8.5.12",
+ "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.12.tgz",
+ "integrity": "sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/acorn": {
+ "version": "8.12.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz",
+ "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==",
+ "dev": true,
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true
+ },
+ "node_modules/ast-metadata-inferer": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/ast-metadata-inferer/-/ast-metadata-inferer-0.8.0.tgz",
+ "integrity": "sha512-jOMKcHht9LxYIEQu+RVd22vtgrPaVCtDRQ/16IGmurdzxvYbDd5ynxjnyrzLnieG96eTcAyaoj/wN/4/1FyyeA==",
+ "dev": true,
+ "dependencies": {
+ "@mdn/browser-compat-data": "^5.2.34"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/browserslist": {
+ "version": "4.23.2",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.2.tgz",
+ "integrity": "sha512-qkqSyistMYdxAcw+CzbZwlBy8AGmS/eEWs+sEV5TnLRGDOL+C5M2EnH6tlZyg0YoAxGJAFKh61En9BR941GnHA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "caniuse-lite": "^1.0.30001640",
+ "electron-to-chromium": "^1.4.820",
+ "node-releases": "^2.0.14",
+ "update-browserslist-db": "^1.1.0"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/bun-types": {
+ "version": "1.1.17",
+ "resolved": "https://registry.npmjs.org/bun-types/-/bun-types-1.1.17.tgz",
+ "integrity": "sha512-Z4+OplcSd/YZq7ZsrfD00DKJeCwuNY96a1IDJyR73+cTBaFIS7SC6LhpY/W3AMEXO9iYq5NJ58WAwnwL1p5vKg==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "~20.12.8",
+ "@types/ws": "~8.5.10"
+ }
+ },
+ "node_modules/bun-types/node_modules/@types/node": {
+ "version": "20.12.14",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.14.tgz",
+ "integrity": "sha512-scnD59RpYD91xngrQQLGkE+6UrHUPzeKZWhhjBSa3HSkwjbQc38+q3RoIVEwxQGRw3M+j5hpNAM+lgV3cVormg==",
+ "dev": true,
+ "dependencies": {
+ "undici-types": "~5.26.4"
+ }
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001643",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001643.tgz",
+ "integrity": "sha512-ERgWGNleEilSrHM6iUz/zJNSQTP8Mr21wDWpdgvRwcTXGAq6jMtOUPP4dqFPTdKqZ2wKTdtB+uucZ3MRpAUSmg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ]
+ },
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+ "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+ "dev": true,
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.3.6",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz",
+ "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==",
+ "dev": true,
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.2.tgz",
+ "integrity": "sha512-kc4r3U3V3WLaaZqThjYz/Y6z8tJe+7K0bbjUVo3i+LWIypVdMx5nXCkwRe6SWbY6ILqLdc1rKcKmr3HoH7wjSQ==",
+ "dev": true
+ },
+ "node_modules/escalade": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz",
+ "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint": {
+ "version": "9.8.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.8.0.tgz",
+ "integrity": "sha512-K8qnZ/QJzT2dLKdZJVX6W4XOwBzutMYmt0lqUS+JdXgd+HTYFlonFgkJ8s44d/zMPPCnOOk0kMWCApCPhiOy9A==",
+ "dev": true,
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.2.0",
+ "@eslint-community/regexpp": "^4.11.0",
+ "@eslint/config-array": "^0.17.1",
+ "@eslint/eslintrc": "^3.1.0",
+ "@eslint/js": "9.8.0",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@humanwhocodes/retry": "^0.3.0",
+ "@nodelib/fs.walk": "^1.2.8",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.2",
+ "debug": "^4.3.2",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^8.0.2",
+ "eslint-visitor-keys": "^4.0.0",
+ "espree": "^10.1.0",
+ "esquery": "^1.5.0",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^8.0.0",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "is-path-inside": "^3.0.3",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.4.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3",
+ "strip-ansi": "^6.0.1",
+ "text-table": "^0.2.0"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ }
+ },
+ "node_modules/eslint-plugin-compat": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-compat/-/eslint-plugin-compat-6.0.0.tgz",
+ "integrity": "sha512-oIynkQYqGnW9ibHl1cGLER8XkUlKaOI8bS80Qz7CjKROvbQm4oN8fWb5l2cG9GJJ4h5eFIHPqkB9ZJuMzwpPFQ==",
+ "dev": true,
+ "dependencies": {
+ "@mdn/browser-compat-data": "^5.5.35",
+ "ast-metadata-inferer": "^0.8.0",
+ "browserslist": "^4.23.1",
+ "caniuse-lite": "^1.0.30001639",
+ "find-up": "^5.0.0",
+ "globals": "^15.7.0",
+ "lodash.memoize": "^4.1.2",
+ "semver": "^7.6.2"
+ },
+ "engines": {
+ "node": ">=18.x"
+ },
+ "peerDependencies": {
+ "eslint": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0"
+ }
+ },
+ "node_modules/eslint-plugin-compat/node_modules/globals": {
+ "version": "15.8.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-15.8.0.tgz",
+ "integrity": "sha512-VZAJ4cewHTExBWDHR6yptdIBlx9YSSZuwojj9Nt5mBRXQzrKakDsVKQ1J63sklLvzAJm0X5+RpO4i3Y2hcOnFw==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.2.tgz",
+ "integrity": "sha512-6E4xmrTw5wtxnLA5wYL3WDfhZ/1bUBGOXV0zQvVRDOtrR8D0p6W7fs3JweNYhwRYeGvd/1CKX2se0/2s7Q/nJA==",
+ "dev": true,
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz",
+ "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==",
+ "dev": true,
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/espree": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-10.1.0.tgz",
+ "integrity": "sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==",
+ "dev": true,
+ "dependencies": {
+ "acorn": "^8.12.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^4.0.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
+ "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
+ "dev": true,
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true
+ },
+ "node_modules/fastq": {
+ "version": "1.17.1",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
+ "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==",
+ "dev": true,
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/file-entry-cache": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
+ "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
+ "dev": true,
+ "dependencies": {
+ "flat-cache": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+ "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
+ "dev": true,
+ "dependencies": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.4"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz",
+ "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
+ "dev": true
+ },
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+ "dev": true
+ },
+ "node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "deprecated": "Glob versions prior to v9 are no longer supported",
+ "dev": true,
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/globals": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
+ "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ignore": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz",
+ "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+ "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+ "dev": true,
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
+ "dev": true,
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-path-inside": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
+ "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true
+ },
+ "node_modules/js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dev": true,
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/json-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+ "dev": true
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true
+ },
+ "node_modules/keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dev": true,
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lodash.memoize": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
+ "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==",
+ "dev": true
+ },
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true
+ },
+ "node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.18",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz",
+ "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==",
+ "dev": true
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "dev": true,
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/optionator": {
+ "version": "0.9.4",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+ "dev": true,
+ "dependencies": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.5"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz",
+ "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==",
+ "dev": true
+ },
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/reusify": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+ "dev": true,
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "node_modules/sax": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz",
+ "integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==",
+ "dev": true
+ },
+ "node_modules/semver": {
+ "version": "7.6.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
+ "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/source-map": {
+ "version": "0.7.4",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
+ "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/stylus": {
+ "version": "0.63.0",
+ "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.63.0.tgz",
+ "integrity": "sha512-OMlgrTCPzE/ibtRMoeLVhOY0RcNuNWh0rhAVqeKnk/QwcuUKQbnqhZ1kg2vzD8VU/6h3FoPTq4RJPHgLBvX6Bw==",
+ "dev": true,
+ "dependencies": {
+ "@adobe/css-tools": "~4.3.3",
+ "debug": "^4.3.2",
+ "glob": "^7.1.6",
+ "sax": "~1.3.0",
+ "source-map": "^0.7.3"
+ },
+ "bin": {
+ "stylus": "bin/stylus"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://opencollective.com/stylus"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/text-table": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
+ "dev": true
+ },
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.5.4",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz",
+ "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==",
+ "peer": true,
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "5.26.5",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
+ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
+ "dev": true
+ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz",
+ "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "escalade": "^3.1.2",
+ "picocolors": "^1.0.1"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/word-wrap": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "dev": true
+ },
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ }
+ }
+}
diff --git a/src/assets/css/button.styl b/src/assets/css/button.styl
index 9f778ded..413e23d0 100644
--- a/src/assets/css/button.styl
+++ b/src/assets/css/button.styl
@@ -132,6 +132,11 @@
}
}
+.screenshot-action-container {
+ display: flex;
+ gap: 0px;
+}
+
.bx-focusable {
position: relative;
overflow: visible;
diff --git a/src/assets/svg/camera-record.svg b/src/assets/svg/camera-record.svg
new file mode 100644
index 00000000..73c35840
--- /dev/null
+++ b/src/assets/svg/camera-record.svg
@@ -0,0 +1,7 @@
+
diff --git a/src/modules/game-bar/action-screenshot.ts b/src/modules/game-bar/action-screenshot.ts
index da060568..5bdf1be3 100644
--- a/src/modules/game-bar/action-screenshot.ts
+++ b/src/modules/game-bar/action-screenshot.ts
@@ -7,24 +7,69 @@ import { Screenshot } from "@/utils/screenshot";
export class ScreenshotAction extends BaseGameBarAction {
$content: HTMLElement;
+ $content2: HTMLElement;
constructor() {
super();
const onClick = (e: Event) => {
- BxEvent.dispatch(window, BxEvent.GAME_BAR_ACTION_ACTIVATED);
- Screenshot.takeScreenshot();
- };
+ BxEvent.dispatch(window, BxEvent.GAME_BAR_ACTION_ACTIVATED);
+ Screenshot.takeScreenshot();
+ };
+
+ const onClickRecord = (e: Event) => {
+ BxEvent.dispatch(window, BxEvent.GAME_BAR_ACTION_ACTIVATED);
+ this.toggleRecording();
+ };
this.$content = createButton({
- style: ButtonStyle.GHOST,
- icon: BxIcon.SCREENSHOT,
- title: t('take-screenshot'),
- onClick: onClick,
- });
+ style: ButtonStyle.GHOST,
+ icon: BxIcon.SCREENSHOT,
+ title: t('take-screenshot'),
+ onClick: onClick,
+ });
+
+ this.$content2 = createButton({
+ style: ButtonStyle.GHOST,
+ icon: BxIcon.RECORD, // Assuming you have a different icon for recording
+ title: t('take-recording'),
+ onClick: onClickRecord,
+ });
+
+ // Add the hotkey listener
+ window.addEventListener('keydown', this.handleHotkey.bind(this));
+ }
+
+ toggleRecording() {
+ if (Screenshot.isRecording) {
+ Screenshot.stopRecording();
+ } else {
+ Screenshot.startRecording();
+ }
+ }
+
+ handleHotkey(event: KeyboardEvent) {
+ if (event.key === '/') {
+ this.toggleRecording();
+ } else if (event.key === '.') {
+ this.recordForOneMinute();
+ }
+ }
+
+ recordForOneMinute() {
+ if (!Screenshot.isRecording) {
+ Screenshot.startRecording();
+ setTimeout(() => {
+ Screenshot.stopRecording();
+ }, 60000); // Stop recording after 60 seconds (1 minute)
+ }
}
render(): HTMLElement {
- return this.$content;
+ const container = document.createElement('div');
+ container.className = 'screenshot-action-container';
+ container.appendChild(this.$content);
+ container.appendChild(this.$content2);
+ return container;
}
}
diff --git a/src/utils/bx-icon.ts b/src/utils/bx-icon.ts
index b76e5943..fcf0e2b4 100644
--- a/src/utils/bx-icon.ts
+++ b/src/utils/bx-icon.ts
@@ -23,6 +23,7 @@ import iconVirtualController from "@assets/svg/virtual-controller.svg" with { ty
import iconCaretLeft from "@assets/svg/caret-left.svg" with { type: "text" };
import iconCaretRight from "@assets/svg/caret-right.svg" with { type: "text" };
import iconCamera from "@assets/svg/camera.svg" with { type: "text" };
+import iconCameraRecord from "@assets/svg/camera-record.svg" with { type: "text" };
import iconMicrophone from "@assets/svg/microphone.svg" with { type: "text" };
import iconMicrophoneMuted from "@assets/svg/microphone-slash.svg" with { type: "text" };
@@ -60,6 +61,7 @@ export const BxIcon = {
CARET_LEFT: iconCaretLeft,
CARET_RIGHT: iconCaretRight,
SCREENSHOT: iconCamera,
+ RECORD: iconCameraRecord,
TOUCH_CONTROL_ENABLE: iconTouchControlEnable,
TOUCH_CONTROL_DISABLE: iconTouchControlDisable,
diff --git a/src/utils/screenshot.ts b/src/utils/screenshot.ts
index e1f72437..de2225b0 100644
--- a/src/utils/screenshot.ts
+++ b/src/utils/screenshot.ts
@@ -9,6 +9,14 @@ export class Screenshot {
static #$canvas: HTMLCanvasElement;
static #canvasContext: CanvasRenderingContext2D;
+ static #mediaRecorder: MediaRecorder | null = null;
+ static #recordedBlobs: Blob[] = [];
+ static #isRecording: boolean = false;
+
+ static get isRecording() {
+ return this.#isRecording;
+ }
+
static setup() {
if (Screenshot.#$canvas) {
return;
@@ -96,4 +104,54 @@ export class Screenshot {
callback && callback();
}, 'image/png');
}
+
+ static async startRecording() {
+ if (this.#isRecording) return;
+
+ const $video = STATES.currentStream.streamPlayer?.getPlayerElement();
+ if (!$video) return;
+
+ // Capture video stream from the video element
+ const videoStream = $video.captureStream();
+
+ // Add audio track(s) from the video element to the video stream
+ const audioTracks = videoStream.getAudioTracks();
+ if (audioTracks.length === 0) {
+ console.warn("No audio tracks found in the video stream");
+ }
+
+ const options = {
+ audioBitsPerSecond: 128000,
+ videoBitsPerSecond: 2500000,
+ mimeType: "video/mp4; codecs=\"avc1.42E01E, mp4a.40.2\"",
+ };
+
+ this.#mediaRecorder = new MediaRecorder(videoStream, options);
+
+ this.#mediaRecorder.ondataavailable = (event) => {
+ if (event.data.size > 0) {
+ this.#recordedBlobs.push(event.data);
+ }
+ };
+ this.#mediaRecorder.onstop = () => {
+ const blob = new Blob(this.#recordedBlobs, { type: 'video/mp4' });
+ const url = URL.createObjectURL(blob);
+ // Save the recorded video to a file or a designated location
+ // For example, you can create a new anchor element and simulate a click to download the video
+ const $anchor = CE('a', { download: 'recorded-video.mp4', href: url });
+ $anchor.click();
+ URL.revokeObjectURL(url);
+ };
+ this.#mediaRecorder.start();
+ this.#isRecording = true;
+ }
+
+ static stopRecording() {
+ if (!this.#isRecording) return;
+
+ this.#mediaRecorder?.stop();
+ this.#mediaRecorder = null; // Reset the MediaRecorder instance
+ this.#recordedBlobs = []; // Reset the recorded blobs array
+ this.#isRecording = false;
+ }
}
diff --git a/src/utils/translation.ts b/src/utils/translation.ts
index dcabe016..f9201035 100644
--- a/src/utils/translation.ts
+++ b/src/utils/translation.ts
@@ -252,6 +252,7 @@ const Texts = {
"support-better-xcloud": "Support Better xCloud",
"swap-buttons": "Swap buttons",
"take-screenshot": "Take screenshot",
+ "take-recording": "Take Recording",
"target-resolution": "Target resolution",
"tc-all-games": "All games",
"tc-all-white": "All white",