Compare commits

...

27 Commits

Author SHA1 Message Date
a6f06fe0f1 Update better-xcloud.user.js 2024-05-19 17:49:17 +07:00
229df61f53 Update translations 2024-05-19 17:49:08 +07:00
0c712b6a31 Update better-xcloud.user.js 2024-05-19 12:14:32 +07:00
f06e36e46b Use PWA version in Android app 2024-05-19 12:14:30 +07:00
1db19f69ac Update better-xcloud.user.js 2024-05-19 11:42:44 +07:00
dc62c13c21 Optimize video player 2024-05-19 11:42:40 +07:00
d5f02550c7 Update better-xcloud.user.js 2024-05-19 10:44:35 +07:00
88b63a5518 App option to disable context menu in Home page 2024-05-19 10:43:44 +07:00
afd851861a Update translations 2024-05-19 07:54:29 +07:00
378f186ee2 Update better-xcloud.user.js 2024-05-18 19:54:49 +07:00
423b171964 Improve emulated mouse's responsiveness 2024-05-18 19:54:43 +07:00
4acf9eba11 Update better-xcloud.user.js 2024-05-18 17:53:39 +07:00
0f5c4f004b Fix button text's vertical alignment in WebView 2024-05-18 17:52:35 +07:00
c7dfacf5c4 Update default MKB preset 2024-05-18 16:06:02 +07:00
0e724b0e4f Update better-xcloud.user.js 2024-05-18 16:00:18 +07:00
47078da413 Fix not fully disable native MKB 2024-05-18 16:00:13 +07:00
e52a296872 Minor fixes 2024-05-18 15:38:03 +07:00
4c593a298e Update better-xcloud.user.js 2024-05-17 18:20:49 +07:00
962b57f0a6 Add option to disable native MKB 2024-05-17 18:19:45 +07:00
22fc730fa1 Update translations 2024-05-17 18:07:08 +07:00
5bd25bf31c Put back the Reload page button into the global settings UI 2024-05-17 17:57:41 +07:00
aba9340e91 Fix HTML issues 2024-05-17 17:24:10 +07:00
d07d6127df Update stylings of global settings UI 2024-05-17 16:57:46 +07:00
e45ed6f9ea Update better-xcloud.user.js 2024-05-15 17:46:45 +07:00
07b477a738 Add "Android app settings" button 2024-05-15 17:46:36 +07:00
fcaab4ce77 Update translations 2024-05-15 17:43:04 +07:00
3954a5d934 Update 01-bug-report.yml 2024-05-13 08:20:14 +07:00
20 changed files with 1076 additions and 621 deletions

View File

@ -9,7 +9,8 @@ body:
value: |
Please fill out the following information to help us resolve the issue.
> [!warning]
> Only use English. Any other languages will be deleted.
> - Only use English. Any other languages will be deleted.
> - Search first before making a report.
- type: dropdown
id: device_type
attributes:

File diff suppressed because it is too large Load Diff

View File

@ -71,9 +71,10 @@
span {
display: inline-block;
height: calc(var(--bx-button-height) - 2px);
height: var(--bx-button-height);
line-height: var(--bx-button-height);
vertical-align: middle;
vertical-align: -webkit-baseline-middle;
color: #fff;
overflow: hidden;
white-space: nowrap;

View File

@ -1,17 +1,6 @@
.bx-settings-reload-button-wrapper {
z-index: var(--bx-reload-button-z-index);
position: fixed;
bottom: 0;
left: 0;
right: 0;
text-align: center;
background: #000000cf;
padding: 10px;
button {
max-width: 450px;
margin: 0 !important;
}
.bx-settings-reload-button {
margin-top: 10px;
height: calc(var(--bx-button-height) * 1.5);
}
.bx-settings-container {
@ -98,34 +87,56 @@
.bx-settings-row {
display: flex;
margin-bottom: 8px;
padding: 2px 4px;
padding: 6px 12px;
position: relative;
label {
flex: 1;
align-self: center;
margin-bottom: 0;
padding-left: 10px;
}
&:focus-within {
@media (hover: none) {
background-color: #242424;
}
&:hover, &:focus-within {
background-color: #242424;
}
input {
align-self: center;
accent-color: var(--bx-primary-button-color);
&:focus {
accent-color: var(--bx-danger-button-color);
}
}
select:disabled {
-webkit-appearance: none;
background: transparent;
text-align-last: right;
border: none;
color: #fff;
select {
&:disabled {
-webkit-appearance: none;
background: transparent;
text-align-last: right;
border: none;
color: #fff;
}
}
input[type=checkbox], 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);
}
}
&:has(input:focus), &:has(select:focus) {
&::before {
content: ' ';
border-radius: 4px;
border: 2px solid #fff;
position: absolute;
top: 0;
left: 0;
bottom: 0;
}
}
}
@ -161,6 +172,10 @@
&:hover {
color: #6dd72b;
}
&:focus {
text-decoration: underline;
}
}
.bx-settings-custom-user-agent {

View File

@ -20,7 +20,6 @@
--bx-danger-button-disabled-color: #a26c6c;
--bx-toast-z-index: 9999;
--bx-reload-button-z-index: 9200;
--bx-dialog-z-index: 9101;
--bx-dialog-overlay-z-index: 9100;
--bx-remote-play-popup-z-index: 9090;

View File

@ -34,11 +34,22 @@ body[data-media-type=tv] .bx-stream-refresh-button {
}
}
div[data-testid=media-container].bx-taking-screenshot:before {
animation: bx-anim-taking-screenshot 0.5s ease;
content: ' ';
position: absolute;
width: 100%;
height: 100%;
z-index: var(--bx-screenshot-animation-z-index);
div[data-testid=media-container] {
display: flex;
&.bx-taking-screenshot:before {
animation: bx-anim-taking-screenshot 0.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;
}

View File

@ -55,7 +55,7 @@ export class Dialog {
}),
),
this.$content = CE('div', {'class': 'bx-dialog-content'}, content),
!hideCloseButton && ($close = CE('button', {}, t('close'))),
!hideCloseButton && ($close = CE('button', {type: 'button'}, t('close'))),
);
$close && $close.addEventListener('click', e => {

View File

@ -285,7 +285,7 @@ export class MkbHandler {
this.#allowStickDecaying = false;
this.#detectMouseStoppedTimeout && clearTimeout(this.#detectMouseStoppedTimeout);
this.#detectMouseStoppedTimeout = window.setTimeout(this.#onMouseStopped.bind(this), 100);
this.#detectMouseStoppedTimeout = window.setTimeout(this.#onMouseStopped.bind(this), 10);
const deltaX = e.movementX;
const deltaY = e.movementY;

View File

@ -127,8 +127,8 @@ export class MkbPreset {
[MkbPresetKey.MOUSE_SENSITIVITY_X]: 50,
[MkbPresetKey.MOUSE_SENSITIVITY_Y]: 50,
[MkbPresetKey.MOUSE_DEADZONE_COUNTERWEIGHT]: 20,
[MkbPresetKey.MOUSE_STICK_DECAY_STRENGTH]: 18,
[MkbPresetKey.MOUSE_STICK_DECAY_MIN]: 6,
[MkbPresetKey.MOUSE_STICK_DECAY_STRENGTH]: 100,
[MkbPresetKey.MOUSE_STICK_DECAY_MIN]: 10,
},
};

View File

@ -426,6 +426,7 @@ export class MkbRemapper {
const $fragment = document.createDocumentFragment();
for (let i = 0; i < keysPerButton; i++) {
$elm = CE('button', {
type: 'button',
'data-prompt': buttonPrompt,
'data-button-index': buttonIndex,
'data-key-slot': i,

View File

@ -55,6 +55,7 @@ const SETTINGS_UI = {
[t('mouse-and-keyboard')]: {
items: [
PrefKey.NATIVE_MKB_DISABLED,
PrefKey.MKB_ENABLED,
PrefKey.MKB_HIDE_IDLE_CURSOR,
],
@ -83,6 +84,7 @@ const SETTINGS_UI = {
[t('ui')]: {
items: [
PrefKey.UI_LAYOUT,
PrefKey.UI_HOME_CONTEXT_MENU_DISABLED,
PrefKey.STREAM_SIMPLIFY_MENU,
PrefKey.SKIP_SPLASH_VIDEO,
!AppInterface && PrefKey.UI_SCROLLBAR_HIDE,
@ -115,7 +117,7 @@ export function setupSettingsUi() {
const PREF_PREFERRED_REGION = getPreferredServerRegion();
const PREF_LATEST_VERSION = getPref(PrefKey.LATEST_VERSION);
let $reloadBtnWrapper: HTMLButtonElement;
let $btnReload: HTMLButtonElement;
// Setup Settings UI
const $container = CE<HTMLElement>('div', {
@ -131,7 +133,12 @@ export function setupSettingsUi() {
'href': SCRIPT_HOME,
'target': '_blank',
}, 'Better xCloud ' + SCRIPT_VERSION),
createButton({icon: BxIcon.QUESTION, label: t('help'), url: 'https://better-xcloud.github.io/features/'}),
createButton({
icon: BxIcon.QUESTION,
style: ButtonStyle.FOCUSABLE,
label: t('help'),
url: 'https://better-xcloud.github.io/features/',
}),
)
);
$updateAvailable = CE('a', {
@ -148,8 +155,20 @@ export function setupSettingsUi() {
$updateAvailable.classList.remove('bx-gone');
}
// Show link to Android app
if (!AppInterface) {
if (AppInterface) {
// Show Android app settings button
const $btn = createButton({
label: t('android-app-settings'),
icon: BxIcon.STREAM_SETTINGS,
style: ButtonStyle.FULL_WIDTH | ButtonStyle.FOCUSABLE,
onClick: e => {
AppInterface.openAppSettings && AppInterface.openAppSettings();
},
});
$wrapper.appendChild($btn);
} else {
// Show link to Android app
const userAgent = UserAgent.getDefault().toLowerCase();
if (userAgent.includes('android')) {
const $btn = createButton({
@ -163,22 +182,21 @@ export function setupSettingsUi() {
}
const onChange = (e: Event) => {
if (!$reloadBtnWrapper) {
return;
}
$reloadBtnWrapper.classList.remove('bx-gone');
// Clear PatcherCache;
PatcherCache.clear();
$btnReload.classList.add('bx-danger');
// Highlight the Settings button in the Header to remind user to reload the page
const $btnHeaderSettings = document.querySelector('.bx-header-settings-button');
$btnHeaderSettings && $btnHeaderSettings.classList.add('bx-danger');
if ((e.target as HTMLElement).id === 'bx_setting_' + PrefKey.BETTER_XCLOUD_LOCALE) {
// Update locale
refreshCurrentLocale();
const $btn = $reloadBtnWrapper.firstElementChild! as HTMLButtonElement;
$btn.textContent = t('settings-reloading');
$btn.click();
$btnReload.textContent = t('settings-reloading');
$btnReload.click();
}
};
@ -226,13 +244,16 @@ export function setupSettingsUi() {
let $control: any;
let $inpCustomUserAgent: HTMLInputElement;
let labelAttrs = {};
let labelAttrs: any = {
tabindex: '-1',
};
if (settingId === PrefKey.USER_AGENT_PROFILE) {
let defaultUserAgent = (window.navigator as any).orgUserAgent || window.navigator.userAgent;
$inpCustomUserAgent = CE('input', {
'type': 'text',
'placeholder': defaultUserAgent,
id: `bx_setting_inp_${settingId}`,
type: 'text',
placeholder: defaultUserAgent,
'class': 'bx-settings-custom-user-agent',
});
$inpCustomUserAgent.addEventListener('change', e => {
@ -254,12 +275,16 @@ export function setupSettingsUi() {
$inpCustomUserAgent.readOnly = !isCustom;
$inpCustomUserAgent.disabled = !isCustom;
onChange(e);
!(e.target as HTMLInputElement).disabled && onChange(e);
});
} else if (settingId === PrefKey.SERVER_REGION) {
let selectedValue;
$control = CE<HTMLSelectElement>('select', {id: `bx_setting_${settingId}`});
$control = CE<HTMLSelectElement>('select', {
id: `bx_setting_${settingId}`,
title: settingLabel,
tabindex: 0,
});
$control.name = $control.id;
$control.addEventListener('change', (e: Event) => {
@ -305,7 +330,12 @@ export function setupSettingsUi() {
} else {
$control = toPrefElement(settingId, onChange);
}
labelAttrs = {'for': $control.id, 'tabindex': 0};
}
if (!!$control.id) {
labelAttrs['for'] = $control.id;
} else {
labelAttrs['for'] = `bx_setting_${settingId}`;
}
// Disable unsupported settings
@ -313,14 +343,19 @@ export function setupSettingsUi() {
($control as HTMLInputElement).disabled = true;
}
// Make disabled control elements un-focusable
if ($control.disabled && !!$control.getAttribute('tabindex')) {
$control.setAttribute('tabindex', -1);
}
const $label = CE('label', labelAttrs, settingLabel);
if (settingNote) {
$label.appendChild(CE('b', {}, settingNote));
}
const $elm = CE<HTMLElement>('div', {'class': 'bx-settings-row'},
$label,
$control
);
$label,
$control,
);
$wrapper.appendChild($elm);
@ -328,28 +363,35 @@ export function setupSettingsUi() {
if (settingId === PrefKey.USER_AGENT_PROFILE) {
$wrapper.appendChild($inpCustomUserAgent!);
// Trigger 'change' event
$control.disabled = true;
$control.dispatchEvent(new Event('change'));
$control.disabled = false;
}
}
}
// Setup Reload button
const $reloadBtn = createButton({
$btnReload = createButton({
label: t('settings-reload'),
style: ButtonStyle.DANGER | ButtonStyle.FOCUSABLE | ButtonStyle.FULL_WIDTH,
classes: ['bx-settings-reload-button'],
style: ButtonStyle.FOCUSABLE | ButtonStyle.FULL_WIDTH,
onClick: e => {
window.location.reload();
$reloadBtn.disabled = true;
$reloadBtn.textContent = t('settings-reloading');
$btnReload.disabled = true;
$btnReload.textContent = t('settings-reloading');
},
});
$reloadBtn.setAttribute('tabindex', '0');
$btnReload.setAttribute('tabindex', '0');
$reloadBtnWrapper = CE<HTMLButtonElement>('div', {'class': 'bx-settings-reload-button-wrapper bx-gone'}, $reloadBtn);
$wrapper.appendChild($reloadBtnWrapper);
$wrapper.appendChild($btnReload);
// Donation link
const $donationLink = CE('a', {'class': 'bx-donation-link', href: 'https://ko-fi.com/redphx', target: '_blank'}, `❤️ ${t('support-better-xcloud')}`);
const $donationLink = CE('a', {
'class': 'bx-donation-link',
href: 'https://ko-fi.com/redphx',
target: '_blank',
tabindex: 0,
}, `❤️ ${t('support-better-xcloud')}`);
$wrapper.appendChild($donationLink);
// Show Game Pass app version

View File

@ -431,43 +431,65 @@ export function updateVideoPlayerCss() {
Screenshot.updateCanvasFilters(filters);
}
const PREF_RATIO = getPref(PrefKey.VIDEO_RATIO);
if (PREF_RATIO && PREF_RATIO !== '16:9') {
if (PREF_RATIO.includes(':')) {
videoCss += `aspect-ratio: ${PREF_RATIO.replace(':', '/')}; object-fit: unset !important;`;
const tmp = PREF_RATIO.split(':');
const ratio = parseFloat(tmp[0]) / parseFloat(tmp[1]);
const maxRatio = window.innerWidth / window.innerHeight;
if (ratio < maxRatio) {
videoCss += 'width: fit-content !important;'
} else {
videoCss += 'height: fit-content !important;'
}
} else {
videoCss += `object-fit: ${PREF_RATIO} !important;`;
}
}
let css = '';
if (videoCss) {
css = `
div[data-testid="media-container"] {
display: flex;
}
#game-stream video {
margin: 0 auto;
align-self: center;
background: #000;
${videoCss}
}
`;
}
$elm.textContent = css;
resizeVideoPlayer();
}
function resizeVideoPlayer() {
const $video = STATES.currentStream.$video;
if (!$video || !$video.parentElement) {
return;
}
const PREF_RATIO = getPref(PrefKey.VIDEO_RATIO);
if (PREF_RATIO.includes(':')) {
const tmp = PREF_RATIO.split(':');
// Get preferred ratio
const videoRatio = parseFloat(tmp[0]) / parseFloat(tmp[1]);
let width = 0;
let height = 0;
// Get parent's ratio
const parentRect = $video.parentElement.getBoundingClientRect();
const parentRatio = parentRect.width / parentRect.height;
// Get target width & height
if (parentRatio > videoRatio) {
height = parentRect.height;
width = height * videoRatio;
} else {
width = parentRect.width;
height = width / videoRatio;
}
// Prevent floating points
width = Math.floor(width);
height = Math.floor(height);
// Update size
$video.style.width = `${width}px`;
$video.style.height = `${height}px`;
$video.style.objectFit = 'fill';
} else {
$video.style.width = '100%';
$video.style.height = '100%';
$video.style.objectFit = PREF_RATIO;
}
}
export function setupStreamUi() {
// Prevent initializing multiple times
if (!document.querySelector('.bx-quick-settings-bar')) {

View File

@ -4,7 +4,7 @@ import { STATES } from "@utils/global";
import { getPref, PrefKey } from "@utils/preferences";
import { UserAgent } from "@utils/user-agent";
enum InputType {
export enum InputType {
CONTROLLER = 'Controller',
MKB = 'MKB',
CUSTOM_TOUCH_OVERLAY = 'CustomTouchOverlay',
@ -38,6 +38,12 @@ export const BxExposed = {
titleInfo = structuredClone(titleInfo);
let supportedInputTypes = titleInfo.details.supportedInputTypes;
// Remove native MKB support on mobile browsers or by user's choice
if (getPref(PrefKey.NATIVE_MKB_DISABLED) || UserAgent.isMobile()) {
supportedInputTypes = supportedInputTypes.filter(i => i !== InputType.MKB);
}
titleInfo.details.hasMkbSupport = supportedInputTypes.includes(InputType.MKB);
if (STATES.hasTouchSupport) {
@ -58,10 +64,7 @@ export const BxExposed = {
gamepadFound && (touchControllerAvailability = 'off');
}
// Remove MKB support on mobile browsers
if (UserAgent.isMobile()) {
supportedInputTypes = supportedInputTypes.filter(i => i !== InputType.MKB);
}
if (touchControllerAvailability === 'off') {
// Disable touch on all games (not native touch)
@ -78,10 +81,10 @@ export const BxExposed = {
titleInfo.details.hasFakeTouchSupport = true;
supportedInputTypes.push(InputType.GENERIC_TOUCH);
}
titleInfo.details.supportedInputTypes = supportedInputTypes;
}
titleInfo.details.supportedInputTypes = supportedInputTypes;
// Save this info in STATES
STATES.currentStream.titleInfo = titleInfo;
BxEvent.dispatch(window, BxEvent.TITLE_INFO_READY);

View File

@ -77,7 +77,7 @@ export const createButton = <T=HTMLButtonElement>(options: BxButton): T => {
$btn.href = options.url;
$btn.target = '_blank';
} else {
$btn = CE('button', {'class': 'bx-button'}) as HTMLButtonElement;
$btn = CE('button', {'class': 'bx-button', type: 'button'}) as HTMLButtonElement;
}
const style = (options.style || 0) as number;

View File

@ -8,6 +8,8 @@ import { TouchController } from "@modules/touch-controller";
import { STATES } from "@utils/global";
import { getPreferredServerRegion } from "@utils/region";
import { GamePassCloudGallery } from "./gamepass-gallery";
import { InputType } from "./bx-exposed";
import { UserAgent } from "./user-agent";
export const NATIVE_FETCH = window.fetch;
@ -188,7 +190,7 @@ class XhomeInterceptor {
let hasTouchSupport = inputConfigs.supportedTabs.length > 0;
if (!hasTouchSupport) {
const supportedInputTypes = inputConfigs.supportedInputTypes;
hasTouchSupport = supportedInputTypes.includes('NativeTouch');
hasTouchSupport = supportedInputTypes.includes(InputType.NATIVE_TOUCH) || supportedInputTypes.includes(InputType.CUSTOM_TOUCH_OVERLAY);
}
if (hasTouchSupport) {
@ -438,6 +440,14 @@ class XcloudInterceptor {
overrides.inputConfiguration = overrides.inputConfiguration || {};
overrides.inputConfiguration.enableVibration = true;
if (getPref(PrefKey.NATIVE_MKB_DISABLED) || UserAgent.isMobile()) {
overrides.inputConfiguration = Object.assign(overrides.inputConfiguration, {
enableMouseInput: false,
enableAbsoluteMouse: false,
enableKeyboardInput: false,
});
}
overrides.videoConfiguration = overrides.videoConfiguration || {};
overrides.videoConfiguration.setCodecPreferences = true;
@ -555,6 +565,29 @@ export function interceptHttpRequests() {
BxEvent.dispatch(window, BxEvent.STREAM_STARTING);
}
// Override experimentals
if (url.startsWith('https://emerald.xboxservices.com/xboxcomfd/experimentation')) {
try {
const response = await NATIVE_FETCH(request, init);
const json = await response.json();
const overrideTreatments: {[key: string]: boolean} = {};
if (getPref(PrefKey.UI_HOME_CONTEXT_MENU_DISABLED)) {
overrideTreatments['EnableHomeContextMenu'] = false;
}
for (const key in overrideTreatments) {
json.exp.treatments[key] = overrideTreatments[key]
}
response.json = () => Promise.resolve(json);
return response;
} catch (e) {
console.log(e);
}
}
// Add list of games with custom layouts to the official list
if (STATES.hasTouchSupport && url.includes('catalog.gamepass.com/sigls/')) {
const response = await NATIVE_FETCH(request, init);

View File

@ -44,6 +44,7 @@ export enum PrefKey {
CONTROLLER_DEVICE_VIBRATION = 'controller_device_vibration',
CONTROLLER_VIBRATION_INTENSITY = 'controller_vibration_intensity',
NATIVE_MKB_DISABLED = 'native_mkb_disabled',
MKB_ENABLED = 'mkb_enabled',
MKB_HIDE_IDLE_CURSOR = 'mkb_hide_idle_cursor',
MKB_ABSOLUTE_MOUSE = 'mkb_absolute_mouse',
@ -64,6 +65,8 @@ export enum PrefKey {
UI_LAYOUT = 'ui_layout',
UI_SCROLLBAR_HIDE = 'ui_scrollbar_hide',
UI_HOME_CONTEXT_MENU_DISABLED = 'ui_home_context_menu_disabled',
VIDEO_CLARITY = 'video_clarity',
VIDEO_RATIO = 'video_ratio',
VIDEO_BRIGHTNESS = 'video_brightness',
@ -318,7 +321,7 @@ export class Preferences {
[PrefKey.BITRATE_VIDEO_MAX]: {
type: SettingElementType.NUMBER_STEPPER,
label: 'Maximum video bitrate',
label: t('bitrate-video-maximum'),
note: '⚠️ ' + t('unexpected-behavior'),
default: 0,
min: 0,
@ -419,6 +422,11 @@ export class Preferences {
},
},
[PrefKey.NATIVE_MKB_DISABLED]: {
label: t('disable-native-mkb'),
default: false,
},
[PrefKey.MKB_DEFAULT_PRESET_ID]: {
default: 0,
},
@ -464,6 +472,11 @@ export class Preferences {
default: false,
},
[PrefKey.UI_HOME_CONTEXT_MENU_DISABLED]: {
label: t('disable-home-context-menu'),
default: false,
},
[PrefKey.BLOCK_SOCIAL_FEATURES]: {
label: t('disable-social-features'),
default: false,

View File

@ -2,6 +2,7 @@ import { STATES } from "@utils/global";
import { BxLogger } from "./bx-logger";
import { TouchController } from "@modules/touch-controller";
import { GamePassCloudGallery } from "./gamepass-gallery";
import { getPref, PrefKey } from "./preferences";
const LOG_TAG = 'PreloadState';
@ -41,6 +42,14 @@ export function overridePreloadState() {
}
}
if (getPref(PrefKey.UI_HOME_CONTEXT_MENU_DISABLED)) {
try {
state.experiments.experimentationInfo.data.treatments.EnableHomeContextMenu = false;
} catch (e) {
BxLogger.error(LOG_TAG, e);
}
}
// @ts-ignore
_state = state;
STATES.appContext = structuredClone(state.appContext);

View File

@ -26,7 +26,10 @@ export enum SettingElementType {
export class SettingElement {
static #renderOptions(key: string, setting: PreferenceSetting, currentValue: any, onChange: any) {
const $control = CE<HTMLSelectElement>('select') as HTMLSelectElement;
const $control = CE<HTMLSelectElement>('select', {
title: setting.label,
tabindex: 0,
}) as HTMLSelectElement;
for (let value in setting.options) {
const label = setting.options[value];
@ -50,7 +53,11 @@ export class SettingElement {
}
static #renderMultipleOptions(key: string, setting: PreferenceSetting, currentValue: any, onChange: any, params: MultipleOptionsParams={}) {
const $control = CE<HTMLSelectElement>('select', {'multiple': true});
const $control = CE<HTMLSelectElement>('select', {
title: setting.label,
multiple: true,
tabindex: 0,
});
if (params && params.size) {
$control.setAttribute('size', params.size.toString());
}
@ -93,7 +100,7 @@ export class SettingElement {
}
static #renderNumber(key: string, setting: PreferenceSetting, currentValue: any, onChange: any) {
const $control = CE('input', {'type': 'number', 'min': setting.min, 'max': setting.max}) as HTMLInputElement;
const $control = CE('input', {'tabindex': 0, 'type': 'number', 'min': setting.min, 'max': setting.max}) as HTMLInputElement;
$control.value = currentValue;
onChange && $control.addEventListener('change', (e: Event) => {
const target = e.target as HTMLInputElement;
@ -108,7 +115,7 @@ export class SettingElement {
}
static #renderCheckbox(key: string, setting: PreferenceSetting, currentValue: any, onChange: any) {
const $control = CE('input', {'type': 'checkbox'}) as HTMLInputElement;
const $control = CE('input', {'type': 'checkbox', 'tabindex': 0}) as HTMLInputElement;
$control.checked = currentValue;
onChange && $control.addEventListener('change', e => {
@ -149,13 +156,30 @@ export class SettingElement {
};
const $wrapper = CE('div', {'class': 'bx-number-stepper'},
$decBtn = CE('button', {'data-type': 'dec'}, '-') as HTMLButtonElement,
$text = CE('span', {}, renderTextValue(value)) as HTMLSpanElement,
$incBtn = CE('button', {'data-type': 'inc'}, '+') as HTMLButtonElement,
);
$decBtn = CE('button', {
'data-type': 'dec',
type: 'button',
tabindex: -1,
}, '-') as HTMLButtonElement,
$text = CE('span', {}, renderTextValue(value)) as HTMLSpanElement,
$incBtn = CE('button', {
'data-type': 'inc',
type: 'button',
tabindex: -1,
}, '+') as HTMLButtonElement,
);
if (!options.disabled && !options.hideSlider) {
$range = CE('input', {'type': 'range', 'min': MIN, 'max': MAX, 'value': value, 'step': STEPS}) as HTMLInputElement;
$range = CE('input', {
id: `bx_setting_${key}`,
type: 'range',
min: MIN,
max: MAX,
value: value,
step: STEPS,
tabindex: 0,
}) as HTMLInputElement;
$range.addEventListener('input', e => {
value = parseInt((e.target as HTMLInputElement).value);
$text.textContent = renderTextValue(value);
@ -282,7 +306,10 @@ export class SettingElement {
const method = SettingElement.#METHOD_MAP[type];
// @ts-ignore
const $control = method(...Array.from(arguments).slice(1)) as HTMLElement;
$control.id = `bx_setting_${key}`;
if (type !== SettingElementType.NUMBER_STEPPER) {
$control.id = `bx_setting_${key}`;
}
// Add "name" property to "select" elements
if (type === SettingElementType.OPTIONS || type === SettingElementType.MULTIPLE_OPTIONS) {

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
import { PrefKey, getPref, setPref } from "@utils/preferences";
import { SCRIPT_VERSION } from "@utils/global";
import { AppInterface, SCRIPT_VERSION } from "@utils/global";
import { UserAgent } from "@utils/user-agent";
/**
@ -38,7 +38,7 @@ export function disablePwa() {
}
// Check if it's Safari on mobile
if (UserAgent.isSafari(true)) {
if (!!AppInterface || UserAgent.isSafari(true)) {
// Disable the PWA prompt
Object.defineProperty(window.navigator, 'standalone', {
value: true,