Compare commits

..

729 Commits

Author SHA1 Message Date
redphx
20afe92371 Bump version to 6.4.2 2025-02-14 06:06:01 +07:00
redphx
5738412f71 Fix crashing in LoadingScreen 2025-02-14 06:05:46 +07:00
redphx
d2ee3d2122 Bump version to 6.4.1 2025-02-08 20:21:20 +07:00
redphx
a65fd8233b Upgrade bun 2025-02-08 20:20:58 +07:00
redphx
1375fb115d Highlight "Bypass region" row in unsupported regions 2025-02-08 20:11:48 +07:00
redphx
bedf82d363 Minify shaders 2025-02-08 11:05:52 +07:00
redphx
b463e4f014 Define types for Patcher 2025-02-08 09:57:42 +07:00
redphx
2f8c776133 Stop using MutationObserver in root-dialog 2025-02-07 22:02:58 +07:00
redphx
585ee82776 Only call useEffect on mounted 2025-02-07 21:32:18 +07:00
redphx
3c2549178b Patch createPortal 2025-02-07 21:31:01 +07:00
redphx
2fb2cfb004 Fix starting StreamStats multiple times 2025-02-07 18:21:07 +07:00
redphx
ac20cc51cc Stop using MutationObserver in stream-ui 2025-02-07 17:31:30 +07:00
redphx
85339f09da Simplify Patcher's logs 2025-02-07 17:10:18 +07:00
redphx
4b06d9fcff useEffect() for Error page 2025-02-07 17:01:03 +07:00
redphx
d4c1e8cce3 Bug fixes 2025-02-07 09:00:22 +07:00
redphx
cf1f656ecf Stop using MutationObserver to track StreamHud's expanded status 2025-02-07 08:43:06 +07:00
redphx
2fd482bb7b Use a better method to show the Better xCloud button ASAP 2025-02-06 21:23:56 +07:00
redphx
63e5e90443 Bump version to 6.4.0 2025-02-05 20:25:15 +07:00
redphx
9034c173e7 Update translations 2025-02-05 20:22:39 +07:00
redphx
5949e1e411 Disable header & footer 2025-02-05 20:11:24 +07:00
redphx
5ce7ade574 Optimize CSS selectors 2025-02-05 17:29:21 +07:00
redphx
e45537adf0 Add EnableWebGPURenderer flag 2025-02-04 21:17:43 +07:00
redphx
f9c9dc9684 Remove "See All Games" 's background color in OLED theme 2025-02-04 21:07:04 +07:00
redphx
ff9a7962c5 Hide Friends section 2025-02-04 20:54:56 +07:00
redphx
d4f070f6bb Allow hiding BYOG section 2025-02-04 20:51:50 +07:00
redphx
66b1f92f4c Disable dropdown's animation 2025-02-04 20:24:46 +07:00
redphx
7a69e7f284 Add OLED theme (#658) 2025-02-04 20:20:48 +07:00
redphx
664e865b82 Hide WebGPU renderer behind EnableWebGPURenderer flag 2025-02-04 19:29:43 +07:00
redphx
7894dea5ff Allow hiding "Recently added, "Leaving soon" and "Genres" sections (#658) 2025-02-03 21:25:38 +07:00
redphx
fd665b6fcd Add WebGPU renderer (#648) 2025-02-02 21:37:21 +07:00
redphx
39ecef976c Optimize WebGL2 2025-02-02 21:12:21 +07:00
redphx
0d5fa0fc96 Optimize WebGPU 2025-02-02 17:57:46 +07:00
redphx
fccd84b7ef Optimize WebGPU 2025-02-02 12:18:00 +07:00
redphx
eb1c027c30 Optimize WebGPU 2025-02-01 20:56:33 +07:00
redphx
6a211db52e Test WebGPU 2025-02-01 17:14:31 +07:00
redphx
17dc7996b1 Replace alwaysTriggerOnChange with onChangeUi 2025-01-30 16:39:52 +07:00
redphx
fe418e6918 Automatically reset game setting's value if it has the same value as global's 2025-01-30 16:10:51 +07:00
redphx
96de61c301 Bump version to 6.3.1 2025-01-29 17:31:25 +07:00
redphx
54a3e144a6 Show "Unknown Game" when unable to get game's title 2025-01-29 15:47:29 +07:00
redphx
277a830d99 Fix unable to reset Virtual controller's preset & Keyboard shortcuts' preset 2025-01-29 15:28:04 +07:00
redphx
0ef8fe18ac Fix calling definition.ready() multiple times 2025-01-29 15:06:34 +07:00
redphx
706665713f Only switch to game settings if it's not empty (#652) 2025-01-29 11:15:51 +07:00
redphx
bf23943da8 Bump version to 6.3.0 2025-01-29 05:26:53 +07:00
redphx
6e31caa4fc Migrate Stream settings in Global storage to Stream storage 2025-01-28 19:22:48 +07:00
redphx
91f9d76c57 Increase title's font-size 2025-01-28 17:20:59 +07:00
redphx
f81627ac7a Update Toast's style 2025-01-28 16:04:53 +07:00
redphx
7c94afacc2 Delete lite.js 2025-01-28 15:34:19 +07:00
redphx
8f37263386 Build pretty.js 2025-01-28 15:15:19 +07:00
redphx
d281db5767 Don't store invalid keys in localStorage 2025-01-28 14:54:57 +07:00
redphx
d638700e03 Update dists 2025-01-28 11:29:42 +07:00
redphx
e3f971845f Game-specific settings (#623) 2025-01-28 11:28:26 +07:00
redphx
91c8172564 Update better-xcloud.user.js 2025-01-28 06:33:10 +07:00
redphx
ee4055e169 Update better-xcloud.user.js 2025-01-28 06:23:22 +07:00
redphx
84415de09f Update better-xcloud.user.js 2025-01-27 19:56:27 +07:00
redphx
d3ef988af7 Fix problem with controller in Settings dialog 2025-01-16 21:50:23 +07:00
redphx
0bf4c289db Bump version to 6.2.1 2025-01-16 20:37:00 +07:00
redphx
c8865bd8a0 Re-arrange patches 2025-01-16 20:26:15 +07:00
redphx
a2f062d9d5 Lite: remove LocalCoOpManager 2025-01-16 20:05:51 +07:00
redphx
b6d4c51ca9 Update dists 2025-01-16 16:49:08 +07:00
redphx
785df72972 Lite: hide unsupported features 2025-01-16 16:37:18 +07:00
redphx
48da8bc527 Update Remote Play dialog's styling 2025-01-16 07:14:52 +07:00
redphx
f9cf02b2da Fix the Y button in default MKB preset 2025-01-16 06:46:12 +07:00
redphx
77e0f2d8ba Lite: disable navigating using gamepad in Settings dialog 2025-01-16 06:45:12 +07:00
redphx
d05a68c470 Fix exception when viewing deviceCode page 2025-01-15 21:30:50 +07:00
redphx
153873e034 Reduce Virtual Controller's input latency 2025-01-08 21:16:07 +07:00
redphx
8d7fbf2804 Bump version to 6.2.0 2025-01-04 19:39:40 +07:00
redphx
488b0dfef2 Show local co-op icon in settings 2025-01-04 18:43:24 +07:00
redphx
b3697df8dc Set background image's quality 2025-01-04 18:30:53 +07:00
redphx
de21549e0d Hide image quality's slider 2025-01-04 13:14:51 +07:00
redphx
097164b92e Set image quality 2025-01-04 12:33:47 +07:00
redphx
3fe6d97133 Update dists 2025-01-04 10:31:45 +07:00
redphx
328fdf46ea Don't render controller icon in game card 2025-01-04 10:31:13 +07:00
redphx
e4dbdea9a5 await requestPointerLock 2025-01-03 20:43:21 +07:00
redphx
f13ce94cf2 Update dists 2025-01-03 20:04:25 +07:00
redphx
a6c19fec15 Use Set() for local co-op list 2025-01-03 20:03:56 +07:00
redphx
6448a00271 Show local co-op icon in details page 2025-01-03 19:49:40 +07:00
redphx
68b29ecb50 Fix not applying class names to local co-op icon 2025-01-03 17:01:51 +07:00
redphx
90f89a0244 Show local co-op icon in game card 2025-01-02 21:39:27 +07:00
redphx
9862f794cf Update button's styling 2024-12-31 06:57:22 +07:00
redphx
e109cdec6a Attempt to fix problem with unadjustedMovement (#628) 2024-12-31 06:52:50 +07:00
redphx
40d1878fb2 Add icon to Better xCloud button 2024-12-29 15:41:35 +07:00
redphx
95f842d9f6 Update 02-feature-request.yml 2024-12-29 08:35:21 +07:00
redphx
d691ea0cf6 Bump version to 6.1.1 2024-12-28 20:46:39 +07:00
redphx
3c05fdcb6d Update README.md 2024-12-28 20:42:51 +07:00
redphx
0cff0b3d3f Update README.md 2024-12-28 20:38:46 +07:00
redphx
6ea47aed48 Add logos 2024-12-28 20:36:56 +07:00
redphx
c8142e5079 Cleanup 2024-12-28 20:36:34 +07:00
redphx
ef85175a91 Update dists 2024-12-28 17:04:40 +07:00
redphx
116640eb32 Fix not releasing pointer lock after quitting the game 2024-12-28 17:04:17 +07:00
redphx
54e28ce350 Fix mouse wheel bug (contd) 2024-12-28 17:04:01 +07:00
redphx
0cd2c02ed6 Update dists 2024-12-28 16:33:25 +07:00
redphx
e585264e8c Fix mouse wheel bug (#600) 2024-12-28 16:33:10 +07:00
redphx
6a133186b8 Check MKB's protocol version 2024-12-28 16:32:52 +07:00
redphx
91b5434952 Update Bx icon 2024-12-27 19:45:37 +07:00
redphx
50e2187e6c Bump version to 6.1.0 2024-12-24 06:58:53 +07:00
redphx
fc1aac66c2 Update translations 2024-12-24 06:46:58 +07:00
redphx
907e595b1e Move BLANK_PRESET_DATA to Table class 2024-12-24 06:43:08 +07:00
redphx
c1786d3fba Only show FL/PL percentage when it's > 1% 2024-12-24 06:31:40 +07:00
redphx
57fb22b905 6.1.0-beta-2 2024-12-23 22:34:03 +07:00
redphx
8b5da5b928 Fix triggering Number Stepper's input event twice 2024-12-23 22:33:38 +07:00
redphx
fe9d9895e9 Render buttons in the correct order 2024-12-23 21:54:27 +07:00
redphx
0fd926eff4 Fix note 2024-12-23 21:49:03 +07:00
redphx
9864954c81 Fix showing incorrect settings when switching customization 2024-12-23 21:35:21 +07:00
redphx
6d1e06dbfe Render controller customization summary 2024-12-23 21:14:39 +07:00
redphx
68d9e7368c Re-arrange buttons 2024-12-23 09:31:18 +07:00
redphx
c0d61a46c6 Update screenshot's prompt glygh 2024-12-23 09:25:04 +07:00
redphx
b143083bdd Stop replacing toUppercCase() in build.ts 2024-12-23 06:36:27 +07:00
redphx
fc5219705c Add BxIconRaw tyoe 2024-12-23 06:26:16 +07:00
redphx
03b7c7358e Optimize CE() 2024-12-23 05:55:11 +07:00
redphx
560a4c309c Use PartialRecord type 2024-12-23 05:38:01 +07:00
redphx
7b60ba3a3e Controller customization feature 2024-12-22 17:17:03 +07:00
redphx
8ef5a95c88 Bump version to 6.0.7 2024-12-18 06:44:35 +07:00
redphx
94c742cbd6 Fix not adding custom controller IDs 2024-12-18 06:32:51 +07:00
redphx
070943e3de Fix "patchBabylonRendererClass" patch 2024-12-18 06:16:44 +07:00
redphx
91deba793c Fix "patchShowSensorControls" patch 2024-12-18 06:08:09 +07:00
redphx
06a9ca9db8 Update 01-bug-report.yml 2024-12-13 07:11:53 +07:00
redphx
b0511d0f7a Update 01-bug-report.yml 2024-12-13 07:09:23 +07:00
redphx
aa35f21763 Update 01-bug-report.yml 2024-12-13 07:08:14 +07:00
redphx
458928d615 Bump version to 6.0.6 2024-12-13 06:32:52 +07:00
redphx
20bf2b1ab6 Turn on "EnableTakControlResizing" flag 2024-12-13 06:32:32 +07:00
redphx
901f55c683 Show different colors for wait time 2024-12-13 06:23:58 +07:00
redphx
15bb18644f Fix wait time stopped showing in game tile (#597) 2024-12-13 05:56:02 +07:00
redphx
873f6546a4 Fix input slider not working with gamepad (#596) 2024-12-13 05:41:25 +07:00
redphx
1db7d4f8d7 Set unadjustedMovement for MKB 2024-12-12 21:37:41 +07:00
redphx
e0b04f306f Bump version to 6.0.5 2024-12-12 06:55:00 +07:00
redphx
a3c948b070 Fix problem with Smart TV profile and Guide menu (#594) 2024-12-12 06:53:25 +07:00
redphx
4e736175b4 Fix Bx button in Guide menu not working 2024-12-12 06:46:41 +07:00
redphx
cb66340177 Fix not showing Bx button in unsupported page 2024-12-12 06:35:07 +07:00
redphx
9f5f7b9d2e Bump version to 6.0.4 2024-12-11 20:37:10 +07:00
redphx
d04742bc25 Update translations 2024-12-11 20:36:51 +07:00
redphx
ed871bbe83 Update dists 2024-12-11 18:59:31 +07:00
redphx
dca8ab9cf6 Fix stats texts 2024-12-11 18:59:24 +07:00
redphx
1bf2f41813 Fix not getting the correct candidate pair 2024-12-11 18:55:28 +07:00
redphx
0fb3b7b7f7 Pad stats 2024-12-11 18:08:28 +07:00
redphx
7709cceff0 Add stat's background opacity 2024-12-11 17:50:04 +07:00
redphx
f8b8012f5c Add "ignoreNewsSection" patch 2024-12-11 17:21:20 +07:00
redphx
1d8517a997 Update <select multiple> CSS 2024-12-11 07:51:41 +07:00
redphx
c893bb2a5d Block notifications 2024-12-11 07:25:48 +07:00
redphx
46469e3949 Fix Guide CSS in TV layout 2024-12-11 06:12:15 +07:00
redphx
d8a085d43f Update suggestion's styles 2024-12-10 21:53:57 +07:00
redphx
b84c464066 Add "Disable features" setting 2024-12-10 21:30:21 +07:00
redphx
f0549b388a Update dists 2024-12-10 20:55:12 +07:00
redphx
9c3b1bd908 Change background color of selected options in <select multiple> 2024-12-10 20:54:53 +07:00
redphx
d671be21ee Also disable Friends feature when blocking social features 2024-12-10 20:53:43 +07:00
redphx
11aefb34d1 Fix overriding features not working 2024-12-10 20:51:27 +07:00
redphx
597cc9782d Always show error log 2024-12-10 20:51:00 +07:00
redphx
61cfd3f8db Alert new server 2024-12-10 20:50:42 +07:00
redphx
a3d5d6a819 Add note for local co-op feature 2024-12-10 20:50:27 +07:00
redphx
ca64b592c5 Bump version to 6.0.3 2024-12-09 20:08:59 +07:00
redphx
d0a8b894b9 Show indicator for current preset 2024-12-09 19:59:30 +07:00
redphx
3230b99a05 Show lock icon in Default preset 2024-12-09 19:36:52 +07:00
redphx
f0e4d4b8d0 Fix exception in app 2024-12-09 18:18:40 +07:00
redphx
d0b84d4591 Update dists 2024-12-09 17:44:24 +07:00
redphx
d292bef5e7 Only show unbind note on custom presets 2024-12-09 17:42:51 +07:00
redphx
5381575048 Add "Xbox button > Press" shortcut 2024-12-09 17:42:11 +07:00
redphx
7206c9e8bc Migrate more events to EventBus 2024-12-09 07:01:13 +07:00
redphx
5fb0dec9f2 Call methods inside app in EventBus 2024-12-08 22:20:46 +07:00
redphx
4ffc034076 Rename EventBus events 2024-12-08 21:57:29 +07:00
redphx
b11d465804 Migrate to EventBus 2024-12-08 21:06:42 +07:00
redphx
e1ba2344b7 Declare window.navigator typing 2024-12-08 20:52:13 +07:00
redphx
8c446ceec3 Refactor patches 2024-12-08 20:26:05 +07:00
redphx
7438375356 Fix disableAdobeAudienceManager() 2024-12-08 20:24:15 +07:00
redphx
741bc9a4e5 Rename EventBus to BxEventBus 2024-12-08 20:09:12 +07:00
redphx
de7bf3edc7 Refactor 2024-12-08 20:05:29 +07:00
redphx
79ebb1a817 EventBus (#590)
* Replace BxEvent.TITLE_INFO_READY with Event Bus

* Migrate more events

* Migrate stream events to event bus

* Migrate preset events

* Migrate more

* Fix dispatching "input" event twice in Number Stepper
2024-12-08 17:55:44 +07:00
redphx
160044c958 Add new domain to ignore 2024-12-08 11:36:35 +07:00
redphx
78c70b5d90 Change "Max FPS" to "Limit FPS" 2024-12-08 11:20:35 +07:00
redphx
9044a07c0b Add note for default presets 2024-12-08 10:49:09 +07:00
redphx
e40d258c79 Bump version to 6.0.2 2024-12-08 07:11:10 +07:00
redphx
3864457a07 Fix applying "disableAbsoluteMouse" patch in the wrong place 2024-12-08 07:08:15 +07:00
redphx
da362325f2 Disable absolute mouse in Android app 2024-12-08 06:50:08 +07:00
redphx
4062852904 Beta 2024-12-07 22:09:22 +07:00
redphx
c426f64ea9 Add generateMsDeviceInfo() 2024-12-07 22:00:50 +07:00
redphx
f7266d6361 Bump version to 6.0.1 2024-12-07 16:56:34 +07:00
redphx
4bd96de89e Update translations 2024-12-07 16:54:31 +07:00
redphx
4011eb402a Linting 2024-12-07 16:48:58 +07:00
redphx
557a38214d Fix native MKB not working in Android app 2024-12-07 10:31:28 +07:00
redphx
4648126f03 Use toFixed(1) in stats 2024-12-07 09:45:47 +07:00
redphx
07b2e47757 Don't show Shortcut button on Android TV 2024-12-07 09:42:29 +07:00
redphx
cf4609d87b Support dynamic resolution with WebGL2 in Genshin 2024-12-07 08:24:44 +07:00
redphx
1ca2b771e7 Fix forcing native MKB not working when mode = "default" 2024-12-07 07:46:13 +07:00
redphx
fe98a1165f Adjust video position (#583) 2024-12-06 21:42:18 +07:00
redphx
4777f90a53 Fix the Shortcut button not showing in product page 2024-12-06 18:16:58 +07:00
redphx
1ea1afe4d4 Optimize Patcher 2024-12-06 18:13:24 +07:00
redphx
fe696043f8 Bump version to 6.0.0 2024-12-05 17:49:53 +07:00
redphx
5b67c344de Bug fixes 2024-12-05 17:18:47 +07:00
redphx
e1f8fcef41 6.0-beta-4 2024-12-05 17:12:48 +07:00
redphx
f9e5ef1b35 Update bun.lockb 2024-12-05 17:11:07 +07:00
redphx
9199351af1 6.0 2024-12-05 17:10:39 +07:00
redphx
c836e33f7b Update better-xcloud.user.js 2024-12-05 07:26:50 +07:00
redphx
0f4195246b Update better-xcloud.user.js 2024-12-04 21:37:53 +07:00
redphx
f3c61191fb Update better-xcloud.user.js 2024-12-04 06:56:19 +07:00
redphx
361e494e11 Update better-xcloud.user.js 2024-12-03 21:11:49 +07:00
redphx
8ab63e6e44 Call frameCallback() ASAP 2024-11-29 09:17:28 +07:00
redphx
a1d6cf97e8 Update build.ts 2024-11-29 09:16:58 +07:00
redphx
fcd6f041e6 Upgrade bun 2024-11-29 09:06:30 +07:00
redphx
2f280cf6e9 Bump version to 5.9.7 2024-11-24 21:32:42 +07:00
redphx
24c3588f1a Update GamePassCloudGallery. ALL sigl 2024-11-24 21:11:27 +07:00
redphx
330b7362ed Bump version to 5.9.6 2024-11-19 06:01:24 +07:00
redphx
5d177bd76c Fix "guideAchievementsDefaultLocked" patch 2024-11-18 22:01:24 +07:00
redphx
f18c5c14ed Bump version to 5.9.5 2024-11-06 19:22:07 +07:00
redphx
d0ceed00f8 Add "Server locations" link 2024-11-06 19:21:38 +07:00
redphx
fce8af4b3b Fix custom buttons disappearing in Guide Menu (#551) 2024-11-06 07:36:32 +07:00
redphx
57686f9d8e Bump version to 5.9.4 2024-11-01 20:08:15 +07:00
redphx
f0e7272a82 Use "brand" instead of "manufacturer" 2024-11-01 19:57:08 +07:00
redphx
b0ecc7171b Update device suggestion URL 2024-11-01 19:52:25 +07:00
redphx
17c08792e1 Redirect to /en-US/play if visiting from an unsupported region 2024-11-01 17:02:30 +07:00
redphx
e8376b52fe Add patch to modify __PRELOADED_STATE__ 2024-11-01 16:55:58 +07:00
redphx
f6581abe34 Show "Remote Play" on web's title instead of "Fortnite" 2024-11-01 08:53:24 +07:00
redphx
b090d325ae Migrate PatcherCache to singleton class 2024-11-01 07:22:21 +07:00
redphx
ec3daa09fd Use hash of client.js file for calculating patch's signature 2024-11-01 07:16:11 +07:00
redphx
b2a2e4d27e Add in-game language support for Bulgarian, Romanian and Thai 2024-10-30 08:45:59 +07:00
redphx
4f3430c43c Bump version to 5.9.3 2024-10-29 20:35:15 +07:00
redphx
15c6d3c74b Update dists 2024-10-29 20:24:04 +07:00
redphx
b170b95145 Fix bugs in NumberStepper 2024-10-29 20:20:34 +07:00
redphx
4217b89194 Reduce the amount of event listeners in NumberStepper 2024-10-29 20:11:54 +07:00
redphx
38211168e9 Reduce width of controller-friendly select box if it has <optgroup> 2024-10-29 16:56:34 +07:00
redphx
392dc2cf86 Categorize servers by continents 2024-10-29 16:51:29 +07:00
redphx
67de264aa9 Revert "Use gl.texSubImage2D()"
This reverts commit 3e2c1bb2a4.
2024-10-27 10:20:11 +07:00
redphx
3e2c1bb2a4 Use gl.texSubImage2D() 2024-10-27 09:36:34 +07:00
redphx
5653914d19 Move WebGL2's drawFrame() function to animate() function 2024-10-26 21:53:03 +07:00
redphx
4a8f66f2a1 Upgrade bun 2024-10-25 08:59:05 +07:00
redphx
70f43ba8f2 Add emoji flag for SwedenCentral server 2024-10-25 07:33:35 +07:00
redphx
4d49639622 Bump version to 5.9.2 2024-10-24 20:58:53 +07:00
redphx
22f1ebdd08 Fix Remote Play not working when using different network (#538) 2024-10-24 20:38:01 +07:00
redphx
bae51eff3d Bump version to 5.9.1 2024-10-23 21:09:26 +07:00
redphx
adc9897210 Update translations 2024-10-23 21:08:20 +07:00
redphx
53442557e1 Fix Virtual Controller Remapper's bug (contd) 2024-10-23 20:51:04 +07:00
redphx
5b67b4c37d Fix Virtual Controller Remapper's bug (contd) 2024-10-23 20:14:10 +07:00
redphx
5a06933143 Update dists 2024-10-23 20:03:26 +07:00
redphx
6440c91cdf Fix Virtual Controller Remapper's bug 2024-10-23 20:00:27 +07:00
redphx
b06dc6e219 Update polling rate's default text 2024-10-22 20:47:45 +07:00
redphx
540a50fb3a Bump version to 5.9.0 2024-10-22 20:14:16 +07:00
redphx
e5178830cb Update translations 2024-10-22 20:13:59 +07:00
redphx
75549bc477 Update dists 2024-10-22 20:08:18 +07:00
redphx
8a3d48d4a3 Optimize Game slug generator by using cached RegEx 2024-10-22 20:07:26 +07:00
redphx
33c3b2810a Update NumberStepper 2024-10-22 16:52:23 +07:00
redphx
95881dd241 Upgrade bun 2024-10-22 16:49:47 +07:00
redphx
c89ebb78a4 Update dists 2024-10-22 10:43:14 +07:00
redphx
222ad1c34e Remove "disableSendMetadata" patch 2024-10-22 10:42:43 +07:00
redphx
6cfff0274d Replace forEach() with for() 2024-10-22 10:42:09 +07:00
redphx
01502363ab Add "disableSendMetadata" patch 2024-10-22 09:14:11 +07:00
redphx
9ab63c4a53 Update polling rate in controller-shortcut.js 2024-10-21 22:16:18 +07:00
redphx
89a968d688 Update dists 2024-10-21 22:01:53 +07:00
redphx
5e98c756d4 Add Polling rate setting 2024-10-21 22:01:32 +07:00
redphx
831fd98d02 Remove CONTROLLER_ENABLE_SHORTCUTS 2024-10-21 20:53:27 +07:00
redphx
de76364a46 Optimize + refactor code 2024-10-21 20:50:12 +07:00
redphx
075b15aa48 Update better-xcloud.user.js 2024-10-21 17:16:45 +07:00
redphx
9388d7fbf4 Update better-xcloud.user.js 2024-10-21 11:04:44 +07:00
redphx
2d8361ba73 Update better-xcloud.user.js 2024-10-21 08:15:49 +07:00
redphx
79c7af10d4 Update better-xcloud.user.js 2024-10-20 21:33:05 +07:00
redphx
6bd658e8a6 Bump version to 5.8.6 2024-10-19 18:54:12 +07:00
redphx
7e6b89b357 Typo 2024-10-19 18:53:49 +07:00
redphx
4271583a5a Add MINIFY_SYNTAX flag in build.ts 2024-10-19 18:46:13 +07:00
redphx
1b2cf70248 Minor fix 2024-10-19 18:40:59 +07:00
redphx
87447df7fd Fix Server badge not updating between sessions 2024-10-19 18:40:47 +07:00
redphx
8664c1a60f Fix taking screenshot not working when limiting FPS 2024-10-19 18:04:05 +07:00
redphx
602c31dc7f Update dist 2024-10-19 16:56:02 +07:00
redphx
bbaea5f629 Fix Game Bar keep clearing focus even when not playing 2024-10-19 16:54:21 +07:00
redphx
03efa528c8 Android: add Shortcut & Wallpaper menu to Game Card's context menu 2024-10-19 16:53:55 +07:00
redphx
63aaca7d61 Fix crashing in "disableTouchContextMenu" patch 2024-10-18 22:19:06 +07:00
redphx
15ae88e9e6 Disable long touch activating context menu 2024-10-18 21:50:22 +07:00
redphx
7578671cc3 Remove "ui_home_context_menu_disabled" setting as it's no longer needed 2024-10-18 21:41:21 +07:00
redphx
82cfb11a6d Update dist 2024-10-18 17:32:08 +07:00
redphx
15700e736d Fix "Smart TV" User-Agent profile (#527) 2024-10-18 16:57:32 +07:00
redphx
b27cfc8215 Refactor utils/html 2024-10-18 16:54:29 +07:00
redphx
1e644504ec Upgrade bun 2024-10-18 16:52:41 +07:00
redphx
7206d11825 Test: hide <video> when using WebGL2 renderer 2024-10-17 20:19:27 +07:00
redphx
fa19a5a68e Bump version to 5.8.5 2024-10-15 19:48:18 +07:00
redphx
3f834f74b6 Update "skipFeedbackDialog" patch 2024-10-15 16:49:38 +07:00
redphx
749d5d720e Update dist 2024-10-14 21:08:35 +07:00
redphx
b969d52a3c Show max FPS value in Stats bar 2024-10-14 21:06:52 +07:00
redphx
e5bd7e64a7 Refactor xCloud & xHome interceptors 2024-10-14 20:08:47 +07:00
redphx
82ee00b4ae Update dist 2024-10-14 17:17:32 +07:00
redphx
8e88af5f8c Set indent of built scripts to 1 space 2024-10-14 17:14:43 +07:00
redphx
927eae3f2f Refactor getInstance() methods 2024-10-14 16:56:05 +07:00
redphx
9f440e9cf4 Don't call animate() when hiding renderer 2024-10-14 16:47:03 +07:00
redphx
1acb30e3af Refactor Game Bar 2024-10-14 16:45:57 +07:00
redphx
34159fad22 Update better-xcloud.lite.user.js 2024-10-13 20:04:42 +07:00
redphx
741538ebcf Bump version to 5.8.4 2024-10-13 20:00:36 +07:00
redphx
6d2e04aff1 Refactor Game Bar actions 2024-10-13 19:15:29 +07:00
redphx
f2bc98229f Update version 2024-10-13 17:46:48 +07:00
redphx
49fb8e2818 Refactor "data-enabled" to "data-activated" 2024-10-13 17:32:38 +07:00
redphx
d012d96675 Add Game Bar action to toggle renderer's visibility 2024-10-13 17:05:27 +07:00
redphx
c129feaf2d Refactor WebGL2Player 2024-10-13 16:26:33 +07:00
redphx
4f7b23912d Refactor BxLogger 2024-10-13 16:06:01 +07:00
redphx
e4d73f9e36 Replace "#" with "private" 2024-10-13 10:51:50 +07:00
redphx
2eea9ce8f5 Bump version to 5.8.3 2024-10-12 18:41:41 +07:00
redphx
27abab8473 Change "FPS" unit to "fps" 2024-10-12 18:41:28 +07:00
redphx
0c34173815 Add "Limit video player's FPS" feature 2024-10-12 16:15:51 +07:00
redphx
0164423e45 Test WebGL2 shader 2024-10-12 11:14:55 +07:00
redphx
71dcaf4b07 Optimize Clarity boost shader 2024-10-11 17:11:32 +07:00
redphx
8f49c48e74 Bump version to 5.8.2 2024-10-11 07:11:37 +07:00
redphx
6fa1f73702 Optimize built scripts 2024-10-10 21:43:42 +07:00
redphx
728abced45 Add jitter stat 2024-10-10 21:35:36 +07:00
redphx
411e43ceb0 Disable inputPollingDurationStats 2024-10-10 20:55:57 +07:00
redphx
baa22dbefc Optimize Clarity Boost shader 2024-10-10 17:28:19 +07:00
redphx
97fb7a114f Set Sharpness's suggested value to 2 2024-10-09 09:02:52 +07:00
redphx
39b2f814b6 Fix stream badge always show "IPv6" even when connecting to IPv4 server #517 2024-10-09 06:30:09 +07:00
redphx
3d34bb3edf Bump version to 5.8.1 2024-10-08 20:00:39 +07:00
redphx
ab1c93eb3a Upgrade bun 2024-10-08 19:59:53 +07:00
redphx
739adfce41 Update translations 2024-10-08 19:55:03 +07:00
redphx
2e77f19006 Update scripts 2024-10-08 07:19:20 +07:00
redphx
8a40d361d9 Add unsupportedNote property 2024-10-08 07:19:09 +07:00
redphx
98fa273b48 Don't render MKB settings on unsupported devices 2024-10-08 07:01:58 +07:00
redphx
1e6527413c Update scripts 2024-10-07 21:40:09 +07:00
redphx
b9134bc141 Add "MSFS2020: force native MKB support" setting 2024-10-07 21:39:42 +07:00
redphx
336a965653 Update translations 2024-10-07 21:21:37 +07:00
redphx
3a91210ba7 Bump version to 5.8.0 2024-10-06 20:35:56 +07:00
redphx
14f2d8a741 Upgrade bun 2024-10-06 20:35:08 +07:00
redphx
c24d1620b6 Update scripts 2024-10-06 20:34:25 +07:00
redphx
63f30111cb Update translations 2024-10-06 20:28:19 +07:00
redphx
d30a628fb1 Update scripts 2024-10-06 20:25:50 +07:00
redphx
5b80170c8b Fix Stream menu's grip handle 2024-10-06 20:20:11 +07:00
redphx
203346c0a1 Fix Quick glancing activated when using Touch control dialog 2024-10-06 20:16:08 +07:00
redphx
9719454ea1 Fix not hiding Stream menu's grip handle sometimes 2024-10-06 20:10:02 +07:00
redphx
59a178bb16 Fix Stats button in Stream menu not updating state 2024-10-06 20:01:53 +07:00
redphx
fd1494ebfa Remove Battery option in unsupported browser 2024-10-06 17:02:18 +07:00
redphx
8e6dec4b70 Update label's style in Stats bar 2024-10-06 16:15:52 +07:00
redphx
6e905621f6 Fix not able to click on checkbox in controller-friendly select box 2024-10-06 15:52:52 +07:00
redphx
76b205a65a New stats: clock, play time, battery, download, upload 2024-10-06 15:50:39 +07:00
redphx
af41dc7c5e Add build.sh 2024-10-05 10:41:18 +07:00
redphx
d0f43db1fd Bump version to 5.7.8 2024-10-02 21:24:23 +07:00
redphx
eed0aa9d9e Fix not disabling unsupported features in Settings dialog 2024-10-02 07:17:17 +07:00
redphx
9007663a3a Lite: remove NativeMkbHandler code in built script 2024-10-01 17:47:01 +07:00
redphx
8f6bc5cb1b Detach VIRTUAL_GAMEPAD_ID from EmulatedMkbHandler 2024-10-01 17:22:33 +07:00
redphx
12d8d766dc Lite: remove XhomeInterceptor and TouchController in built script 2024-10-01 17:09:07 +07:00
redphx
aeffccaf67 Update better-xcloud.lite.user.js 2024-10-01 16:51:44 +07:00
redphx
b2736d574d Disable PatcherCache in Lite version 2024-10-01 16:49:40 +07:00
redphx
98cf893956 Fix Settings dialog opening during gameplay 2024-09-30 17:18:40 +07:00
redphx
086afafedf Update dist 2024-09-30 17:12:22 +07:00
redphx
bd58355ef5 Create better-xcloud.lite.user.js 2024-09-30 17:11:05 +07:00
redphx
109cd63a7b Bump version to 5.7.7 2024-09-26 19:50:28 +07:00
redphx
8ea6b7f81a Update better-xcloud.user.js 2024-09-26 19:49:58 +07:00
redphx
e7c10d43f5 Fix buttons layout in product details page 2024-09-26 19:46:30 +07:00
redphx
2f7a57e084 Update translations 2024-09-26 19:22:51 +07:00
redphx
c99e38b097 Update better-xcloud.user.js 2024-09-25 20:20:45 +07:00
redphx
f6ec6d7c9b Fix not calculating controller-friendly <select>'s size when switching tab 2024-09-25 20:20:06 +07:00
redphx
e69fa19ef3 Update better-xcloud.user.js 2024-09-25 19:44:33 +07:00
redphx
cc422b31a4 build: collapse if/else blocks without curly braces 2024-09-25 19:43:19 +07:00
redphx
9609d0ae7b Fix duplicated CSS strings 2024-09-25 19:43:07 +07:00
redphx
506fd71433 Update better-xcloud.user.js 2024-09-25 08:48:57 +07:00
redphx
f40b8cb0b2 build: add more minify steps 2024-09-25 08:47:01 +07:00
redphx
49a6c036a3 Bump version to 5.7.6 2024-09-24 21:13:56 +07:00
redphx
f5a5a79a82 Check offscreen element in isElementVisible() 2024-09-24 20:58:32 +07:00
redphx
7ec449160a Update better-xcloud.user.js 2024-09-24 19:53:20 +07:00
redphx
fecc5411da Remote Play dialog: update styles 2024-09-24 19:53:02 +07:00
redphx
f704452171 Remote Play dialog: replace radio buttons with select box 2024-09-24 19:47:55 +07:00
redphx
135193813c Shorten language names 2024-09-24 19:34:20 +07:00
redphx
bb57f72e64 Calculate minimum width of controller-friendly <select> elements 2024-09-24 19:31:56 +07:00
redphx
69d7cbfffb Bump version to 5.7.5 2024-09-20 17:46:32 +07:00
redphx
92e6828cb2 Update better-xcloud.user.js 2024-09-20 17:25:12 +07:00
redphx
12ad81e9c7 Update translations 2024-09-20 17:16:32 +07:00
redphx
102e0bd318 Use "let" keyword in Patcher to reduce the size of generated script 2024-09-20 16:53:48 +07:00
redphx
9308963bc2 Remote Play: Prevent adding "Fortnite" to the "Jump back in" list 2024-09-20 16:42:27 +07:00
redphx
c90e013dc1 Upgrade bun 2024-09-20 16:42:03 +07:00
redphx
037927b9be Fix not able to control Remote Play dialog using controller (#509) 2024-09-20 07:05:39 +07:00
redphx
dabab9acb1 Bump version to 5.7.4 2024-09-19 19:59:12 +07:00
redphx
a4a52c6bc3 Update better-xcloud.user.js 2024-09-19 19:58:49 +07:00
redphx
eebd7434ea Remove Close icon in Remote Play dialog 2024-09-19 19:58:45 +07:00
redphx
ec1805f832 Refactor Remote Play 2024-09-19 18:01:27 +07:00
redphx
34f959d5ae Update better-xcloud.user.js 2024-09-18 20:15:02 +07:00
redphx
784a31ce43 Migrate Remote Play popup to Navigation dialog 2024-09-18 20:14:49 +07:00
redphx
df266d32fc Update better-xcloud.user.js 2024-09-12 22:03:35 +07:00
redphx
a6ccd6666e Check next Remote Play server when the console list is empty 2024-09-12 22:03:21 +07:00
redphx
fe609034d6 Remote Play: don't accept candidates with port 0 2024-09-11 08:24:50 +07:00
redphx
97ec29faa0 Upgrade bun 2024-09-11 08:09:27 +07:00
redphx
a34ae75131 Bump version to 5.7.3 2024-09-07 18:36:05 +07:00
redphx
139543aaa5 Update better-xcloud.user.js 2024-09-07 18:29:45 +07:00
redphx
8099115959 Set Achievements list's default filter to "Locked" 2024-09-07 18:15:04 +07:00
redphx
21efa5ffdc Minor fix in Game Bar 2024-09-07 17:27:23 +07:00
redphx
07ebf3926b Update script in app when clicking on the "Version x available" button 2024-09-07 16:43:56 +07:00
redphx
714178a82d Bump version to 5.7.2 2024-09-06 20:55:12 +07:00
redphx
5c2c13e0e6 Update better-xcloud.user.js 2024-09-06 20:52:35 +07:00
redphx
3f423325b9 Add Game Bar action to mute/unmute speaker (#491) 2024-09-06 20:44:28 +07:00
redphx
870a205ead Update better-xcloud.user.js 2024-09-06 18:17:39 +07:00
redphx
421af05882 Update TA button's logic & layout in the Guide Menu 2024-09-06 18:07:13 +07:00
redphx
756d105f74 Clear focus on Game Bar after activating it 2024-09-06 17:03:55 +07:00
redphx
4d90ebca68 Bump version to 5.7.1 2024-09-05 06:39:19 +07:00
redphx
1297230192 Update better-xcloud.user.js 2024-09-05 06:34:57 +07:00
redphx
a45d0f8b98 Update buttons layout in Guide Menu with TV layout (#492) 2024-09-05 06:34:30 +07:00
redphx
821904066b Fix no sound when using volume control feature (#490) 2024-09-05 06:17:23 +07:00
redphx
15b7869e5d Bump version to 5.7.0 2024-09-04 20:53:37 +07:00
redphx
2ed4e23c87 Update better-xcloud.user.js 2024-09-04 20:19:38 +07:00
redphx
e952bf07c8 Fix problem with "|" character in game title 2024-09-04 20:19:31 +07:00
redphx
8d44dab04d Update better-xcloud.user.js 2024-09-04 19:45:02 +07:00
redphx
6a792548fa Update TrueAchievements button in Guide Menu 2024-09-04 19:44:41 +07:00
redphx
29f6413306 Support suggesting boolean settings 2024-09-04 16:59:18 +07:00
redphx
53d67616c3 Fix not clearing states when quitting game while queueing 2024-09-04 16:43:39 +07:00
redphx
03ad02bd4d Don't show the "Close app" button in Guide Menu when playing 2024-09-04 16:42:52 +07:00
redphx
110106aa97 Update better-xcloud.user.js 2024-09-04 07:31:40 +07:00
redphx
7310700dbb Add button to download wallpapers in app 2024-09-03 19:56:34 +07:00
redphx
5a0ef88237 Update better-xcloud.user.js 2024-09-03 16:57:17 +07:00
redphx
a6e358479a Integrate TrueAchievements 2024-09-03 16:56:58 +07:00
redphx
4b02fec8ac Update better-xcloud.user.js 2024-09-03 16:50:32 +07:00
redphx
93e3f1fa49 Update better-xcloud.user.js 2024-09-03 10:19:43 +07:00
redphx
ae9a1a68d4 Update better-xcloud.user.js 2024-09-02 21:25:14 +07:00
redphx
adf6b05c10 Update better-xcloud.user.js 2024-09-02 21:18:32 +07:00
redphx
e0489d30bb Update better-xcloud.user.js 2024-09-02 20:22:08 +07:00
redphx
9f46eca956 Minify SVG in generated JS 2024-09-02 14:57:03 +07:00
redphx
4888c399f0 Upgrade bun 2024-09-02 10:44:36 +07:00
redphx
e372db8dd9 Update better-xcloud.user.js 2024-08-31 19:03:58 +07:00
redphx
5ba4a669e6 Compress Loading Screen's CSS 2024-08-31 19:02:36 +07:00
redphx
26b28564cc Optimize Guide Menu's buttons 2024-08-31 17:03:42 +07:00
redphx
ad0be634d2 Update better-xcloud.user.js 2024-08-31 10:25:58 +07:00
redphx
6f460302cf Fix Game Bar not showing sometimes 2024-08-31 09:57:49 +07:00
redphx
24f0cf18d9 Bump version to 5.6.1 2024-08-30 20:24:04 +07:00
redphx
2df8274233 Update better-xcloud.user.js 2024-08-30 20:18:18 +07:00
redphx
a095370ab8 Show the wait time of every games in the "Jump back in" section all at once 2024-08-30 20:04:40 +07:00
redphx
339447d29c Update Settings dialog's style 2024-08-30 20:04:11 +07:00
redphx
efe0caf02f Update better-xcloud.user.js 2024-08-29 21:34:17 +07:00
redphx
6daabea288 Add troubleshooting link 2024-08-29 21:30:27 +07:00
redphx
772a642283 Update translations 2024-08-29 21:03:42 +07:00
redphx
675fc8431c Don't build meta.js for beta version 2024-08-29 17:44:14 +07:00
redphx
9a97053662 Upgrade bun 2024-08-29 17:38:39 +07:00
redphx
9d6190668b Bump version to 5.6.0 2024-08-26 18:10:37 +07:00
redphx
ba0b804720 Update z-index 2024-08-26 17:49:45 +07:00
redphx
1fe1f74ad5 Update better-xcloud.user.js 2024-08-26 17:28:22 +07:00
redphx
4f7e0a4f7f Add "Suggest settings" feature 2024-08-26 17:27:34 +07:00
redphx
070113b764 Update better-xcloud.user.js 2024-08-26 07:52:38 +07:00
redphx
c669b80914 Update better-xcloud.user.js 2024-08-26 07:43:23 +07:00
redphx
1e8e7f0030 Update better-xcloud.user.js 2024-08-25 17:19:53 +07:00
redphx
edc26e366e Update better-xcloud.user.js 2024-08-25 16:02:03 +07:00
redphx
13bd258f2a Update better-xcloud.user.js 2024-08-25 10:10:11 +07:00
redphx
a1b6fc111c Update better-xcloud.user.js 2024-08-25 10:08:03 +07:00
redphx
366c7c8ea4 Update better-xcloud.user.js 2024-08-25 10:05:57 +07:00
redphx
7b5bb1e342 Update better-xcloud.user.js 2024-08-23 17:21:47 +07:00
redphx
131da53d25 Update better-xcloud.user.js 2024-08-22 07:37:15 +07:00
redphx
7ae90cb5b3 Update better-xcloud.user.js 2024-08-20 20:48:09 +07:00
redphx
812e2390d0 Update bun.lockb 2024-08-14 18:47:40 +07:00
redphx
4e133582e4 Bump version to 5.5.6 2024-08-14 18:47:13 +07:00
redphx
8ca6a9e08c Update better-xcloud.user.js 2024-08-14 18:26:23 +07:00
redphx
344b6bb2c9 Dispatch "TvRemoteBack" in backButtonPressed() 2024-08-14 18:26:19 +07:00
redphx
8b56ae218d Fix disabling touch control doesn't always work 2024-08-14 17:52:09 +07:00
redphx
3d2b887859 Update better-xcloud.user.js 2024-08-14 08:52:37 +07:00
redphx
370fc7b2c2 Upgrade bun 2024-08-14 08:52:22 +07:00
redphx
5f4a1c24f0 Fix touch border 2024-08-14 08:51:38 +07:00
redphx
382cd1aa51 Fix Settings button keep being added/removed from header 2024-08-14 08:51:23 +07:00
redphx
d929a958ff Bump version to 5.5.5 2024-08-10 18:43:39 +07:00
redphx
a81c6621a8 Update .bx-settings-row background 2024-08-09 21:50:34 +07:00
redphx
edc11b3b48 Update better-xcloud.user.js 2024-08-09 07:20:46 +07:00
redphx
c333fffab7 Fix not disconnecting StreamUiHandler's MutationObserver (#477) 2024-08-09 07:20:43 +07:00
redphx
8c904897b8 Add Korea IP 2024-08-09 06:53:03 +07:00
redphx
683709f980 Upgrade bun 2024-08-09 06:41:14 +07:00
redphx
4562ef8f19 Bump verstion to 5.5.4 2024-08-06 20:29:43 +07:00
redphx
2fcf14c5b9 Fix touch problem with Stream Menu 2024-08-06 20:24:40 +07:00
redphx
c1af19072d Switch to WebGL canvas context 2024-08-06 19:51:16 +07:00
redphx
5dc6f0c2f6 Fix StreamMenu not displaying correctly 2024-08-06 19:48:54 +07:00
redphx
3ba9565c3e Bump version to 5.5.3 2024-08-05 17:40:20 +07:00
redphx
2d6c56e25c Update better-xcloud.user.js 2024-08-04 17:48:16 +07:00
redphx
95d5fb8ed7 Rearrange settings 2024-08-04 17:45:15 +07:00
redphx
7dfe61f4ca Refactor SettingDefinition 2024-08-04 17:37:30 +07:00
redphx
3f66c1298e Update better-xcloud.user.js 2024-08-04 17:04:56 +07:00
redphx
6ab24e9231 Refactor StreamUiHandler 2024-08-04 12:33:03 +07:00
redphx
619d70d3cb Update better-xcloud.user.js 2024-08-03 17:20:27 +07:00
redphx
fb123e00d7 Fix Settings button not showing on Header sometimes 2024-08-03 17:04:54 +07:00
redphx
39f7ee6ddb Add "detectBrowserRouterReady" patch 2024-08-02 20:47:28 +07:00
redphx
5db35cdcc9 Bug fixes 2024-08-02 07:19:27 +07:00
redphx
8c7e4650d4 Create PatcherUtils 2024-08-02 07:07:59 +07:00
redphx
a77460e242 Bump version to 5.5.2 2024-08-02 05:57:10 +07:00
redphx
d2839b2b7c Fix crashing when hiding "Play with touch" section 2024-08-02 05:56:35 +07:00
redphx
8aa5177e10 Update 02-feature-request.yml 2024-08-01 19:28:54 +07:00
redphx
ff490be713 Fix Settings dialog not showing full settings when signed in 2024-08-01 19:23:57 +07:00
redphx
eb340e7f2a Update Device Code page's CSS 2024-08-01 17:51:37 +07:00
redphx
654862fd1c Bump version to 5.5.1 2024-07-31 17:45:45 +07:00
redphx
ddb234673c Update better-xcloud.user.js 2024-07-31 17:43:46 +07:00
redphx
e822072836 Open Settings dialog on Unsupported page 2024-07-31 17:31:26 +07:00
redphx
362638ff0c Fix not setting default User-Agent correctly 2024-07-31 17:31:05 +07:00
redphx
b4a94c95c0 Fix CSS of focus border + shortcut button 2024-07-31 08:47:34 +07:00
redphx
a996c0e367 Update better-xcloud.user.js 2024-07-31 07:39:58 +07:00
redphx
09a2c86ad4 Fix macros/build.renderStylus() not loading CSS each build 2024-07-31 07:39:39 +07:00
redphx
0d3385790c Show fullscreen text when reloading page 2024-07-31 07:37:23 +07:00
redphx
a39d056eba Render Settings footer in lite mode 2024-07-31 06:54:14 +07:00
redphx
847adb1fff Compress CSS 2024-07-31 06:27:43 +07:00
redphx
b49ee400f1 Close Settings dialog when opening App settings 2024-07-31 06:08:00 +07:00
redphx
ab91323abd Rearrange visual quality & resolution options 2024-07-30 18:34:57 +07:00
redphx
74237dbd24 Minor fixes 2024-07-30 18:31:47 +07:00
redphx
825db798db Update better-xcloud.user.js 2024-07-30 18:23:49 +07:00
redphx
41fe12afc6 Try to fix Remote Play issue 2024-07-30 18:23:17 +07:00
redphx
361ce057b7 Minor fixes 2024-07-30 18:06:40 +07:00
redphx
9fad2914ac Fix Settings sometimes not being injected to header 2024-07-28 15:58:03 +07:00
redphx
eb42f4a3d3 Update better-xcloud.user.js 2024-07-28 10:51:24 +07:00
redphx
857c7ec0c3 Update build script 2024-07-28 10:51:01 +07:00
redphx
8d559a53a8 Disable AAM 2024-07-28 09:41:23 +07:00
redphx
13323cce24 Minor fix 2024-07-28 09:07:45 +07:00
redphx
03eb323fd9 Implement es-lint-plugin-compat 2024-07-28 09:00:31 +07:00
redphx
fd21fe63f7 Remove disableTrackEvent() patch 2024-07-28 08:02:36 +07:00
redphx
857b63a9f9 Remove unused flags 2024-07-28 07:25:11 +07:00
redphx
40006c5931 Bump version to 5.5.0 2024-07-27 16:45:47 +07:00
redphx
8742da0531 Fix not disabling the Reload button correctly 2024-07-27 16:33:40 +07:00
redphx
6e17c2e24b Fix not showing default touch control 2024-07-27 16:29:03 +07:00
redphx
6a81ee2806 Add Danish 2024-07-27 16:24:09 +07:00
redphx
9dfdeb8f12 Merge Global settings and Stream settings into one dialog 2024-07-27 16:09:13 +07:00
redphx
023799232e Update better-xcloud.user.js 2024-07-27 15:46:25 +07:00
redphx
a44714ed29 Update better-xcloud.user.js 2024-07-27 11:36:48 +07:00
redphx
70d5d62890 Update better-xcloud.user.js 2024-07-27 10:49:29 +07:00
redphx
5d8dd4e3a9 Update better-xcloud.user.js 2024-07-27 08:02:44 +07:00
redphx
60526d5166 Update better-xcloud.user.js 2024-07-27 06:36:28 +07:00
redphx
40794f6088 Update better-xcloud.user.js 2024-07-26 21:53:04 +07:00
redphx
4de3fd9228 Update better-xcloud.user.js 2024-07-26 21:38:09 +07:00
redphx
d75f65e2d2 Update better-xcloud.user.js 2024-07-26 20:07:41 +07:00
redphx
21b9b2f661 Update better-xcloud.user.js 2024-07-26 19:48:51 +07:00
redphx
fc6f610859 Update better-xcloud.user.js 2024-07-26 18:00:08 +07:00
redphx
231febc0ad Update better-xcloud.user.js 2024-07-26 08:05:06 +07:00
redphx
e3bd341e57 Update better-xcloud.user.js 2024-07-25 20:26:29 +07:00
redphx
a0996eee77 Update better-xcloud.user.js 2024-07-25 08:55:18 +07:00
redphx
f46722e540 Update better-xcloud.user.js 2024-07-24 20:48:45 +07:00
redphx
1ec162115f Try to fix crashing on iOS (#455) 2024-07-21 06:17:12 +07:00
redphx
5a27caad23 Bump version to 5.4.2 2024-07-20 07:18:58 +07:00
redphx
e7d7ccf165 Add VIDEO_POWER_PREFERENCE value to Debug info 2024-07-20 07:13:44 +07:00
redphx
782c0a6967 Update better-xcloud.user.js 2024-07-20 07:10:22 +07:00
redphx
5f696ff0b8 Update translations 2024-07-20 07:04:31 +07:00
redphx
c796152bdd Focus the other button when reaching the beginning/end 2024-07-20 06:54:53 +07:00
redphx
2ae8452c90 Update layout 2024-07-20 06:39:43 +07:00
redphx
bf7d6453ea Fix layout of the "Create shortcut" button 2024-07-20 06:30:26 +07:00
redphx
130a7ffbd7 Put "low-power" before "high-performance" 2024-07-20 05:56:34 +07:00
redphx
1d590103ce Fix not able to scroll pass hidden settings 2024-07-20 05:52:55 +07:00
redphx
a268e49280 Fix unexpected "false" texts 2024-07-20 05:43:32 +07:00
redphx
7db004ede3 Fix issue with <select multiple> and BxSelect element 2024-07-19 21:10:28 +07:00
redphx
6a8eecab06 Bump version to 5.4.1 2024-07-19 18:25:57 +07:00
redphx
640dd2fb5a Update better-xcloud.user.js 2024-07-19 18:12:35 +07:00
redphx
30bb8cfbeb Fix not able to loop around in some cases 2024-07-19 18:08:39 +07:00
redphx
42b57a2cf8 Set controller-friendly UI as default on Android TV 2024-07-19 18:01:41 +07:00
redphx
210fdfbabe Add "GPU configuration" setting 2024-07-19 17:41:52 +07:00
redphx
dbbdc48aab Add "Create shortcut" button to Product Details page 2024-07-19 16:48:31 +07:00
redphx
66123bc4ef Fix BxSelect element not showing label correctly (#449) 2024-07-19 06:54:00 +07:00
redphx
2ecd995e47 Minor updates 2024-07-19 06:24:17 +07:00
redphx
0e03d4dc32 Update better-xcloud.user.js 2024-07-18 20:48:21 +07:00
redphx
5b4088cc81 Show debug info 2024-07-18 20:47:58 +07:00
redphx
1f3e4b8250 Re-arrange buttons in Guide menu 2024-07-18 20:08:59 +07:00
redphx
daf3f72736 Loop around settings 2024-07-18 17:30:34 +07:00
redphx
fbebb12965 Close Stream settings dialog when not clicking on any child elements 2024-07-18 09:20:40 +07:00
redphx
43ef2b7cd0 Update better-xcloud.user.js 2024-07-18 09:06:45 +07:00
redphx
e1eca20792 Fix Stream settings dialog in portrait mode 2024-07-18 09:06:37 +07:00
redphx
c2d8f1fbf7 Disable Fire OS's "Update required" screen 2024-07-18 07:21:35 +07:00
redphx
64be526b2d Bump version to 5.4.0 2024-07-17 18:13:09 +07:00
redphx
13527b9cf6 Bug fixes 2024-07-17 18:08:41 +07:00
redphx
6999783c07 Update better-xcloud.user.js 2024-07-17 17:56:18 +07:00
redphx
0f88396db8 Allow navigating Stream settings using controller/keyboard all the time 2024-07-17 17:54:06 +07:00
redphx
e73b4dfe78 Support navigating Stream settings using left stick 2024-07-17 17:47:23 +07:00
redphx
0fb83de0ff Add "Reload page" button to the Guide menu even when not playing 2024-07-17 17:43:57 +07:00
redphx
714276e552 Hide Stream settings when navigating to another pages 2024-07-17 17:40:08 +07:00
redphx
58b83c4eb2 Add BX_EXPOSED.backButtonPressed() 2024-07-17 17:38:59 +07:00
redphx
585ec4a598 Update translations and add support for Traditional Chinese 2024-07-17 17:38:27 +07:00
redphx
816249e9a5 Minor fix 2024-07-17 08:04:19 +07:00
redphx
30421fcdba Update better-xcloud.user.js 2024-07-17 07:59:09 +07:00
redphx
7f43db03df Press LB/RB to focus setting tabs 2024-07-17 07:58:51 +07:00
redphx
742fd24b8c Fix bugs with Clarity boost select box 2024-07-17 07:48:36 +07:00
redphx
2db246e081 Update layout 2024-07-17 07:18:55 +07:00
redphx
d8e87e5c2c Reduce polling rate 2024-07-17 06:50:13 +07:00
redphx
d7dc6931d6 Only disable buttons in number-stepper when they're at min/max 2024-07-17 06:49:19 +07:00
redphx
44083f2469 Update better-xcloud.user.js 2024-07-16 21:53:04 +07:00
redphx
64568532cb Allow controlling settings using gamepad 2024-07-16 21:52:44 +07:00
redphx
2a0af5d0ab Make Controller shortcuts settings controller-friendly 2024-07-16 17:59:21 +07:00
redphx
b66cb448ec Make Stream settings dialog controller-friendly 2024-07-16 17:08:56 +07:00
redphx
be338f3e34 Update bx-select's layout 2024-07-15 21:18:51 +07:00
redphx
394dc68ece Add "Controller-friendly UI" option 2024-07-15 20:54:35 +07:00
redphx
66120d6970 Update better-xcloud.user.js 2024-07-15 17:12:22 +07:00
redphx
368a6f726a Add optionsGroup 2024-07-15 17:10:07 +07:00
redphx
7409956616 Show Settings button in header when not signed in 2024-07-15 17:04:04 +07:00
redphx
d41fd22a47 Update servers 2024-07-15 09:13:23 +07:00
redphx
55a56837c8 Bump version to 5.3.0 2024-07-14 17:53:35 +07:00
redphx
df713136d8 Update better-xcloud.user.js 2024-07-14 17:51:37 +07:00
redphx
29dfdaf72e Show allocation time instead of total wait time 2024-07-14 17:51:32 +07:00
redphx
04cf66a466 Update better-xcloud.user.js 2024-07-14 16:44:38 +07:00
redphx
1d55026c6d Add option to show wait time in game card 2024-07-14 16:44:18 +07:00
redphx
fcfecf7ff9 Update global-settings.styl 2024-07-14 09:18:06 +07:00
redphx
5e22bf097a Optimize checkHeader() 2024-07-14 09:17:12 +07:00
redphx
542079d53e Update translations 2024-07-13 20:15:36 +07:00
redphx
1d00d793b8 Bump version to 5.2.0 2024-07-13 18:27:51 +07:00
redphx
2a9da6f827 Update better-xcloud.user.js 2024-07-13 18:24:18 +07:00
redphx
b6089a61f9 Update translations 2024-07-13 18:22:43 +07:00
redphx
0fe6608be9 Disable "patchSetCurrentlyFocusedInteractable" patch 2024-07-13 18:07:09 +07:00
redphx
9e39e80309 Fix watchHeader() being called multiple times 2024-07-13 18:04:17 +07:00
redphx
5bfcf3a044 Refactor header.ts 2024-07-13 18:00:15 +07:00
redphx
66d5d9edc6 Disable the region selection box when the server lis is empty 2024-07-13 17:41:27 +07:00
redphx
9d00082c67 Disable "EnableWifiWarnings" flag 2024-07-13 17:35:45 +07:00
redphx
ef2e0892bc Add setting to bypass region restriction 2024-07-13 17:27:40 +07:00
redphx
ce1901b300 Refactor network.ts 2024-07-13 16:19:33 +07:00
redphx
18a8b8330c Add "patchSetCurrentlyFocusedInteractable" patch 2024-07-13 16:07:53 +07:00
redphx
b9e78f09d3 Update better-xcloud.user.js 2024-07-12 06:20:02 +07:00
redphx
33b2b36e2b Revert "Remove website's version detection"
This reverts commit 91ab57fa29.
2024-07-12 06:19:30 +07:00
redphx
61ed68c40f Update better-xcloud.user.js 2024-07-10 07:18:18 +07:00
redphx
4ad0d44929 Disable touch for non-touch supported User-Agent profile 2024-07-10 07:18:10 +07:00
redphx
422442071e Bump version to 5.1.3 2024-07-09 18:31:18 +07:00
redphx
8d1ae0656c Update better-xcloud.user.js 2024-07-09 07:53:46 +07:00
redphx
a78de2ca37 Hide Stream settings when the Guide menu is shown (#441) 2024-07-09 07:53:36 +07:00
redphx
db1da22c0a Remove SMART_TV_UNIQUE_ID from SMART_TV_GENERIC profile 2024-07-09 07:48:05 +07:00
redphx
91ab57fa29 Remove website's version detection 2024-07-09 07:46:42 +07:00
redphx
416307e23a Fix "alwaysShowStreamHud" not working on non-TV devices 2024-07-08 20:05:20 +07:00
redphx
e7c94f3ece Update better-xcloud.user.js 2024-07-08 18:06:23 +07:00
redphx
ea9ad16770 Don't show negative packetLost 2024-07-08 18:02:07 +07:00
redphx
9a2e7de68d Update better-xcloud.user.js 2024-07-08 17:55:01 +07:00
redphx
962f4dec6d Minor update 2024-07-08 17:45:38 +07:00
redphx
10d0dedc0a Add "alwaysShowStreamHud" patch 2024-07-08 17:17:28 +07:00
redphx
c6acc251ae Update bun 2024-07-08 08:06:54 +07:00
redphx
a06d061409 Update better-xcloud.user.js 2024-07-08 07:32:33 +07:00
redphx
6b2412ff27 Disable Onboarding screen 2024-07-08 07:32:16 +07:00
redphx
0f360d4be1 Bump version to 5.1.2 2024-07-07 21:37:15 +07:00
redphx
900ab38153 Minor fixes 2024-07-07 19:20:58 +07:00
redphx
c03c63f3c3 Update better-xcloud.user.js 2024-07-07 18:23:37 +07:00
redphx
d4f4084991 Disable "Most popular" option 2024-07-07 18:22:41 +07:00
redphx
975549b4e7 Add option to hide "All games" section 2024-07-07 18:16:50 +07:00
redphx
345d0f78dc Add option to hide "News" section 2024-07-07 16:55:57 +07:00
redphx
938dfa6aaa Add option to hide "Friends" section 2024-07-07 16:43:56 +07:00
redphx
d7ed9e1603 Add "ignorePlayWithFriendsSection" patch 2024-07-07 16:14:08 +07:00
redphx
224e98829d Improve "enableXcloudLogger" patch 2024-07-07 15:28:33 +07:00
redphx
56a3f1d8c8 Bug fixes 2024-07-07 14:59:12 +07:00
redphx
d82a38c0f1 Update better-xcloud.user.js 2024-07-07 11:21:22 +07:00
redphx
77729789e3 Fix problems in the Guide menu #436 #438 2024-07-07 11:20:43 +07:00
redphx
5763701355 Update better-xcloud.user.js 2024-07-06 20:58:08 +07:00
redphx
cafeed1a3c Bug fixes 2024-07-06 20:48:27 +07:00
redphx
691f116ea0 Add "enableTvRoutes" patch 2024-07-06 17:14:02 +07:00
redphx
481b365e6e Add "IsSupportedTvBrowser" flag 2024-07-06 16:13:53 +07:00
redphx
2b63edb7eb Refactor browser & userAgent's capabilities 2024-07-06 15:53:01 +07:00
redphx
b6746598a3 Update better-xcloud.user.js 2024-07-13 12:26:53 +07:00
redphx
45bda4bb24 Fix script not being loaded after refreshing token 2024-07-13 12:19:07 +07:00
redphx
c93db035f3 Update ICE candidates 2024-07-06 11:08:41 +07:00
redphx
e75fa397ee Bump version to 5.1.1 2024-07-02 18:11:08 +07:00
redphx
98a9f4fc37 Update better-xcloud.user.js 2024-07-02 18:10:52 +07:00
redphx
dee8c9dbd0 Refactor buttons in guide-menu 2024-07-02 18:06:33 +07:00
redphx
d31a06be89 Use {once: true} in some event listeners 2024-07-02 17:20:23 +07:00
redphx
277c777121 Add "Show controller connection status" setting 2024-07-02 17:08:40 +07:00
redphx
385fd71e86 Update better-xcloud.user.js 2024-07-02 06:49:40 +07:00
redphx
986d9fe088 Show "Stream settings" and "App settings" in the Guide menu 2024-07-02 06:41:23 +07:00
redphx
6de235ce2f Fix overriding experimentation stopped working 2024-07-02 05:50:02 +07:00
redphx
f027565534 Bump version to 5.1.0 2024-07-01 18:08:46 +07:00
redphx
0213b860fd No longer need "Kiwi Browser v123" profile 2024-07-01 17:52:12 +07:00
redphx
13feb36aae Update better-xcloud.user.js 2024-07-01 17:44:49 +07:00
redphx
d83261d816 Dim Stream settings' overlay when not playing 2024-07-01 17:42:42 +07:00
redphx
c1502b5552 Prepare for webOS & Tizen support 2024-07-01 17:26:04 +07:00
redphx
64d60aedfa Create bun.lockb 2024-07-01 17:23:13 +07:00
redphx
889a97e56b Stop using setCodecPreferences() as it causes stuttering on Chromium 124+ 2024-07-01 17:22:24 +07:00
redphx
7aee4d5148 Compress CSS 2024-07-01 17:20:39 +07:00
redphx
2000d6d80e Update translations 2024-06-26 21:00:38 +07:00
redphx
297c0848d5 Bump version to 5.0.1 2024-06-26 18:14:57 +07:00
redphx
51ef9f9e8f Update package.json 2024-06-26 18:14:29 +07:00
redphx
9717315b79 Update README.md 2024-06-26 08:44:16 +07:00
redphx
e176ef6fc0 Update package.json 2024-06-23 17:59:24 +07:00
redphx
52694d8f8e Update better-xcloud.user.js 2024-06-23 17:30:00 +07:00
redphx
b7928ebe68 Fix stream badge showing "1h60m" instead of "2h" 2024-06-23 17:27:11 +07:00
redphx
05eddce11e Update better-xcloud.user.js 2024-06-22 16:43:22 +07:00
redphx
057da5b3ea Fix exception with navigator.vibrate() on start up 2024-06-22 16:43:18 +07:00
redphx
11ef014c74 Update build.ts 2024-06-22 16:30:22 +07:00
redphx
fa82f0ba95 Update better-xcloud.user.js 2024-06-22 16:30:02 +07:00
redphx
36db8db1e7 Minify syntax in dist file 2024-06-22 16:29:54 +07:00
redphx
d906de7803 Update better-xcloud.user.js 2024-06-22 10:36:27 +07:00
redphx
cf546123db Show "Off" when Sharpness is 0 2024-06-22 10:36:02 +07:00
redphx
d6a4d1741b Update NumberStepper 2024-06-22 10:35:45 +07:00
redphx
22e7400e06 Bump version to 5.0.0 2024-06-21 18:10:50 +07:00
redphx
f169c17e18 Add WebGL2 renderer 2024-06-21 17:45:43 +07:00
redphx
6150c2ea70 Update better-xcloud.user.js 2024-06-20 20:46:15 +07:00
redphx
2cdf92b159 Update better-xcloud.user.js 2024-06-19 18:17:42 +07:00
redphx
6f6a9e223e Show WS error in toast 2024-06-10 08:57:07 +07:00
redphx
f71904c30b Bump version to 4.7.1 2024-06-10 08:29:53 +07:00
redphx
3a16187504 Update Guide menu detection 2024-06-10 08:03:13 +07:00
redphx
00ebb3f672 Fix dispatching STREAM_PLAYING event when playing normal video 2024-06-09 18:31:57 +07:00
redphx
ebb4d3c141 Add FeatureGates 2024-06-09 18:31:15 +07:00
redphx
902918d7fb Update URLs 2024-06-09 15:56:32 +07:00
redphx
b780e4e63b Minor fix 2024-06-09 15:45:41 +07:00
redphx
32889e0cf1 Minor fix 2024-06-09 15:43:19 +07:00
redphx
d8b9fcc951 Update better-xcloud.user.js 2024-06-09 15:40:15 +07:00
redphx
c7734245ae Don't check update for beta version 2024-06-09 11:50:46 +07:00
redphx
35e7fdacb5 Disable context menu on devices with touch support by default 2024-06-09 11:48:12 +07:00
redphx
504f16b802 Rename "hasTouchSupport" to "userAgentHasTouchSupport" 2024-06-09 11:47:08 +07:00
redphx
a3a7a57b51 Get PointerServer's port from the app 2024-06-09 11:41:00 +07:00
redphx
0d59ab2ee2 Bump version to 4.7.0 2024-06-08 17:22:43 +07:00
redphx
ff794c44b5 Update better-xcloud.user.js 2024-06-08 17:21:55 +07:00
redphx
ccc824d544 Add vscode files 2024-06-08 17:05:02 +07:00
redphx
eb8490a798 Add native MKB support for Android app 2024-06-08 17:04:49 +07:00
redphx
a41d0cda0c Update better-xcloud.user.js 2024-06-07 21:04:52 +07:00
redphx
559c3c52c3 Update better-xcloud.user.js 2024-06-07 07:57:10 +07:00
redphx
2ed1e8735f Update better-xcloud.user.js 2024-06-07 07:20:25 +07:00
redphx
03d5550f05 Update better-xcloud.user.js 2024-06-06 20:53:53 +07:00
redphx
fb1ce5306d Update better-xcloud.user.js 2024-06-05 21:37:38 +07:00
redphx
e8e37aa575 Update better-xcloud.user.js 2024-06-05 18:28:31 +07:00
redphx
9f1f28a2d7 Update better-xcloud.user.js 2024-06-03 15:52:55 +07:00
redphx
44cf4f1d19 Fix video ratio not working properly 2024-06-03 15:52:52 +07:00
redphx
bb20f408a3 Update custom-flags.user.js 2024-06-03 05:44:58 +07:00
redphx
c03737e224 Update custom-flags.user.js 2024-06-02 11:13:47 +07:00
redphx
5b137f7791 Update 01-bug-report.yml 2024-06-02 10:54:14 +07:00
redphx
7f52479f0a Update better-xcloud.user.js 2024-06-02 10:44:23 +07:00
redphx
2e0a59cbe1 Add back the ability to use native MKB feature on unofficial titles 2024-06-02 10:43:59 +07:00
redphx
850afb4ca7 Disable xCloud analytics also remove the Feedback button in the Guide menu 2024-06-02 09:57:04 +07:00
redphx
e98fa29271 Disable social features also hide the "Start a party" button in the Guide menu 2024-06-02 09:52:41 +07:00
redphx
d79aaecb54 Bump version to 4.6.3 2024-06-01 18:39:30 +07:00
redphx
148b60cccb Update better-xcloud.user.js 2024-06-01 18:39:08 +07:00
redphx
db78918d34 Don't process further when vibration intensity is 0 2024-06-01 18:37:06 +07:00
redphx
ddc4346da8 Update better-xcloud.user.js 2024-06-01 18:28:04 +07:00
redphx
4db25e8d62 Don't show stream badges in the Guide menu until xCloud removes the Stream menu 2024-06-01 18:27:46 +07:00
redphx
e10a98c245 Fix not disabling vibration when intensity is 0 2024-06-01 18:26:23 +07:00
redphx
c9b070253c Fix button styles in WebView 2024-06-01 18:23:14 +07:00
redphx
ba07e0498e Fix disabling the MKB dialog not making it go away 2024-06-01 18:23:00 +07:00
redphx
6c8f336e9c Update better-xcloud.user.js 2024-06-01 17:29:11 +07:00
redphx
522e4dddd2 Update home icon 2024-06-01 17:29:09 +07:00
redphx
e1627dca61 Update better-xcloud.user.js 2024-06-01 17:11:53 +07:00
redphx
b5a19cd211 Replace double-quote with single-quote in SVG files 2024-06-01 17:11:29 +07:00
redphx
d5d81f3374 Add "Back to home" button in the Stream menu 2024-06-01 17:04:33 +07:00
redphx
2db78d01a0 Hide xCloud's Home button in the Guide menu 2024-06-01 16:34:54 +07:00
redphx
8f9976da28 Add "Reload stream" & "Back to home" buttons in the Guide menu 2024-06-01 16:29:01 +07:00
redphx
322418ec5b Reposition badges in the Guide menu 2024-06-01 16:00:11 +07:00
redphx
28049e5d22 Update package.json 2024-06-01 15:49:30 +07:00
redphx
9593cdf8dd Update better-xcloud.user.js 2024-06-01 10:21:29 +07:00
redphx
732bd19f3a Add Catalan 2024-06-01 10:21:11 +07:00
redphx
562c1c95f5 Update translations 2024-06-01 10:21:05 +07:00
redphx
5d1a0a3428 Update better-xcloud.user.js 2024-06-01 10:12:52 +07:00
redphx
758501bcd3 Change objectFit to "contain" 2024-06-01 10:12:45 +07:00
redphx
e10eadc832 Fix first time activating controller shortcut will also open the Guide menu (#409) 2024-06-01 10:11:53 +07:00
redphx
ed3c4041ff Show stats in the Guide menu & refactor 2024-06-01 10:11:06 +07:00
redphx
60cadb4b04 Update better-xcloud.user.js 2024-05-31 21:29:36 +07:00
redphx
b5c033498e Move "AUDIO_MIC_ON_PLAYING" setting 2024-05-31 07:31:36 +07:00
redphx
a24446a6b4 Update better-xcloud.user.js 2024-05-31 07:22:01 +07:00
redphx
bee190b867 Use a better method to skip feedback dialog 2024-05-31 07:14:58 +07:00
redphx
941ed0a00f Fix loading screen not working properly 2024-05-31 06:55:31 +07:00
redphx
1f632db6b4 Bump version to 4.6.2 2024-05-30 17:24:50 +07:00
redphx
c07e3297ca Update better-xcloud.user.js 2024-05-30 17:22:19 +07:00
redphx
5e43915ff7 Add a Disable button in the MKB dialog 2024-05-30 17:22:06 +07:00
redphx
e21375821d Update better-xcloud.user.js 2024-05-30 16:46:56 +07:00
redphx
6438e533d6 Hide rocket animation in Smart TV profile 2024-05-30 16:46:48 +07:00
redphx
e9671cbe5d Fix video not being full screen (#415) 2024-05-30 16:28:05 +07:00
redphx
b99ec65cc9 Update better-xcloud.user.js 2024-05-30 09:34:47 +07:00
redphx
addcf56abf Minor fix 2024-05-30 09:22:04 +07:00
redphx
db17bda673 Bump version to 4.6.1 2024-05-30 07:09:39 +07:00
redphx
0a60119c3b Update better-xcloud.user.js 2024-05-30 07:09:14 +07:00
redphx
ef14c78941 Fix settings being reset after refreshing page 2024-05-30 07:04:01 +07:00
redphx
f2dc102996 Update better-xcloud.user.js 2024-05-29 20:19:43 +07:00
redphx
02db103a72 Fix pink border when using Clarity feature in Logitech G Cloud 2024-05-29 20:19:36 +07:00
redphx
f291047b64 Update better-xcloud.user.js 2024-05-29 20:09:22 +07:00
redphx
5866644673 Clear TABs when disabling touch control 2024-05-29 20:09:20 +07:00
250 changed files with 33017 additions and 17437 deletions

0
.github/FUNDING.yml vendored Normal file → Executable file
View File

53
.github/ISSUE_TEMPLATE/01-bug-report.yml vendored Normal file → Executable file
View File

@@ -4,6 +4,13 @@ title: "[Bug] "
labels: labels:
- bug - bug
body: body:
- type: markdown
attributes:
value: |
> [!note]
> - Use `Discussions` if you want to ask for question.
> - Non-English reports will be deleted. No exceptions.
- type: checkboxes - type: checkboxes
id: checklist id: checklist
attributes: attributes:
@@ -11,12 +18,41 @@ body:
options: options:
- label: I will only use English in my report. - label: I will only use English in my report.
required: true required: true
- label: "The bug doesn't happen when I disable Better xCloud script."
required: true
- label: I have used the search function for [**open and closed issues**](https://github.com/redphx/better-xcloud/issues?q=is%3Aissue) to see if someone else has already submitted the same bug report. - label: I have used the search function for [**open and closed issues**](https://github.com/redphx/better-xcloud/issues?q=is%3Aissue) to see if someone else has already submitted the same bug report.
required: true required: true
- label: I will describe the problem with as much detail as possible. - label: I will describe the problem with as much detail as possible.
required: true required: true
- type: dropdown
id: question_01
attributes:
label: xCloud officially supports your country/region
options:
- "No"
- "Yes"
validations:
required: true
- type: dropdown
id: question_02
attributes:
label: "The bug doesn't happen when you disable Better xCloud script"
options:
- "No"
- "Yes"
validations:
required: true
- type: dropdown
id: question_03
attributes:
label: "Previous Better xCloud versions didn't have this bug (name which one)"
options:
- "No"
- "Yes"
validations:
required: true
- type: dropdown - type: dropdown
id: device_type id: device_type
attributes: attributes:
@@ -31,6 +67,7 @@ body:
multiple: false multiple: false
validations: validations:
required: true required: true
- type: input - type: input
id: device_name id: device_name
attributes: attributes:
@@ -39,6 +76,7 @@ body:
placeholder: "e.g., Google Pixel 8" placeholder: "e.g., Google Pixel 8"
validations: validations:
required: true required: true
- type: input - type: input
id: os id: os
attributes: attributes:
@@ -47,14 +85,16 @@ body:
placeholder: "e.g., Android 14" placeholder: "e.g., Android 14"
validations: validations:
required: true required: true
- type: input - type: input
id: browser_version id: browser_version
attributes: attributes:
label: "Browser Version" label: "Android app/Browser Version"
description: "What is the name and version of the browser?" description: "What is the name and version of the browser/Android app?"
placeholder: "e.g., Chrome 124.0" placeholder: "e.g., Chrome 124.0, Android app 0.15.0"
validations: validations:
required: true required: true
- type: input - type: input
id: extension_version id: extension_version
attributes: attributes:
@@ -63,6 +103,7 @@ body:
placeholder: "e.g., 3.5.0" placeholder: "e.g., 3.5.0"
validations: validations:
required: true required: true
- type: input - type: input
id: game_list id: game_list
attributes: attributes:
@@ -71,6 +112,7 @@ body:
placeholder: "e.g., Halo" placeholder: "e.g., Halo"
validations: validations:
required: false required: false
- type: textarea - type: textarea
id: reproduction id: reproduction
attributes: attributes:
@@ -84,6 +126,7 @@ body:
3. Error 3. Error
validations: validations:
required: true required: true
- type: textarea - type: textarea
id: media id: media
attributes: attributes:

41
.github/ISSUE_TEMPLATE/02-feature-request.yml vendored Normal file → Executable file
View File

@@ -13,49 +13,30 @@ body:
- type: dropdown - type: dropdown
id: device_type id: device_type
attributes: attributes:
label: Device label: Device type
description: "Which device are you using?" description: "Which device type is this feature for?"
options: options:
- All devices
- Phone/Tablet - Phone/Tablet
- Laptop
- Desktop - Desktop
- TV - TV
- Other
multiple: false multiple: false
validations: validations:
required: true required: true
- type: dropdown
id: os - type: input
id: device_name
attributes: attributes:
label: "Operating System" label: "Device"
description: "Which operating system is it running?" description: "Name of the device"
options: placeholder: "e.g., Google Pixel 8"
- Windows
- macOS
- Linux
- Android
- iOS/iPadOS
- Other
multiple: false
validations:
required: true
- type: dropdown
id: browser
attributes:
label: "Browser"
description: "Which browser are you using?"
options:
- Chrome/Edge/Chromium
- Kiwi Browser
- Safari
- Other
multiple: false
validations: validations:
required: true required: true
- type: textarea - type: textarea
id: suggestion id: suggestion
attributes: attributes:
label: "Suggestion" label: "Suggestion"
description: "What do you want to suggest?" description: "What do you want to suggest? Include (mockup) screenshot if possible."
validations: validations:
required: true required: true

0
.github/ISSUE_TEMPLATE/config.yml vendored Normal file → Executable file
View File

2
.gitignore vendored Normal file → Executable file
View File

@@ -1,3 +1,5 @@
src/modules/patcher/patches/*.js
# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore # Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore
# Logs # Logs

12
.vscode/settings.json vendored Executable file
View File

@@ -0,0 +1,12 @@
{
"files.readonlyInclude": {
"dist/**/*": true,
"src/modules/patcher/patches/controller-customization.js": true,
"src/modules/patcher/patches/expose-stream-session.js": true,
"src/modules/patcher/patches/game-card-icons.js": true,
"src/modules/patcher/patches/local-co-op-enable.js": true,
"src/modules/patcher/patches/poll-gamepad.js": true,
"src/modules/patcher/patches/remote-play-keep-alive.js": true,
"src/modules/patcher/patches/vibration-adjust.js": true
}
}

15
.vscode/tasks.json vendored Executable file
View File

@@ -0,0 +1,15 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "typescript",
"tsconfig": "tsconfig.json",
"option": "watch",
"problemMatcher": [
"$tsc-watch"
],
"group": "build",
"label": "tsc: watch - tsconfig.json"
}
]
}

1
LICENSE Normal file → Executable file
View File

@@ -1,6 +1,7 @@
MIT License MIT License
Copyright (c) 2023 redphx Copyright (c) 2023 redphx
Copyright (c) 2023 Advanced Micro Devices, Inc.
Copyright (c) 2020 Phosphor Icons Copyright (c) 2020 Phosphor Icons
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy

68
README.md Normal file → Executable file
View File

@@ -1,70 +1,40 @@
# Better xCloud <div align="center">
Improve Xbox Cloud Gaming (xCloud) experience on [xbox.com/play](https://www.xbox.com/play). It also allows you to use Remote Play on the xCloud website. <img src="https://raw.githubusercontent.com/redphx/better-xcloud/refs/heads/typescript/resources/logos/better-xcloud.png" width="256"/>
<h1>Better xCloud</h1>
<!-- Latest Version Badge -->
<a href="https://github.com/redphx/better-xcloud/releases"><img src="https://img.shields.io/github/v/release/redphx/better-xcloud?label=latest" alt="Latest version" /></a>
<!-- Total Downloads Badge -->
<a href="https://github.com/redphx/better-xcloud/releases"><img src="https://img.shields.io/github/downloads/redphx/better-xcloud/total?color=%23e15f2c" alt="Total downloads" /></a>
<!-- Total Stars Badge -->
<a href="https://github.com/redphx/better-xcloud/stargazers"><img src="https://img.shields.io/github/stars/redphx/better-xcloud?color=%23cca400" alt="Total stars" /></a>
</div>
> [!TIP] ### Improve Xbox Cloud Gaming (xCloud) experience on [xbox.com/play](https://www.xbox.com/play). It also allows you to use Remote Play on the xCloud website.
> The Android app is in development at [redphx/better-xcloud-android](https://github.com/redphx/better-xcloud-android)
> [!IMPORTANT] > [!IMPORTANT]
> I don't accept pull requests at the moment (except PR for custom touch controls) > I only accept pull requests for:
> - Custom touch controls
> - Bug fixes
**Supported platforms:** **Supported platforms:**
- Windows - Windows
- macOS - macOS
- Linux, SteamOS (including Steam Deck) - Linux, SteamOS (including Steam Deck)
- Android, Android TV (including Meta Quest VR Headsets) - Android, Android TV (including Meta Quest VR Headsets): [redphx/better-xcloud-android](https://github.com/redphx/better-xcloud-android)
- iOS, iPadOS - iOS, iPadOS
This script makes me spend more time with xCloud, and I hope the same thing happens to you. This script makes me spend more time with xCloud, and I hope the same thing happens to you.
If you like this project please give it a 🌟. Thank you 🙏. If you like this project please give it a 🌟. Thank you 🙏.
[![Latest version](https://img.shields.io/github/v/release/redphx/better-xcloud?label=latest)](https://github.com/redphx/better-xcloud/releases) ## How to install
[![Total downloads](https://img.shields.io/github/downloads/redphx/better-xcloud/total?color=%23e15f2c)](https://github.com/redphx/better-xcloud/releases) Visit the [home page](https://better-xcloud.github.io) to know how to install Better xCloud on your device.
[![Total stars](https://img.shields.io/github/stars/redphx/better-xcloud?color=%23cca400)](https://github.com/redphx/better-xcloud/stargazers)
## Full documentations ## Full documentations
- For the full details please visit: https://better-xcloud.github.io - For the full details please visit: [**better-xcloud.github.io**](https://better-xcloud.github.io)
- [Demo video](https://youtu.be/hyp69Jrb2sQ) - [Demo video](https://youtu.be/hyp69Jrb2sQ)
⚠️ Please DO NOT report **Better xCloud**'s bugs on [/r/xcloud subreddit](https://reddit.com/r/xcloud/). Report bugs in [Issues](https://github.com/redphx/better-xcloud/issues) or [Telegram channel](https://t.me/betterxcloud) instead. ⚠️ Please DO NOT report **Better xCloud**'s bugs on [/r/xcloud subreddit](https://reddit.com/r/xcloud/). Report bugs in [Issues](https://github.com/redphx/better-xcloud/issues) or [Telegram channel](https://t.me/betterxcloud) instead.
## Table of Contents
- [**How to install**](#how-to-install)
- [**Features**](#features)
- [**Donation**](#donation)
- [**Acknowledgements**](#acknowledgements)
- [**Disclaimers**](#disclaimers)
## How to install
Visit [this page](https://better-xcloud.github.io/browsers) to know how to install Better xCloud on your device.
## Features
<img width="400" alt="Settings UI" src="https://github.com/redphx/better-xcloud/assets/96280/4bec2d62-31df-499c-9aad-2485626b6925">
<br>
<img width="400" alt="Remote Play dialog" src="https://github.com/redphx/better-xcloud/assets/96280/daf7f698-a228-4f9c-8f23-9669e061a64c">
<br>
<img width="600" alt="Stream HUD" src="https://github.com/redphx/better-xcloud/assets/96280/51bdb96c-79ab-402f-902a-a9e6229973b2">
<br>
<img width="600" alt="Stream settings" src="https://github.com/redphx/better-xcloud/assets/96280/ed513cb3-6e6c-4e8e-9e06-c62e71e41c90">
<br>
<img width="600" alt="Remapper" src="https://github.com/redphx/better-xcloud/assets/96280/f2e2bc51-f673-4b24-b127-c7169b86462b">
&nbsp;
**Demo video:** [https://youtu.be/oDr5Eddp55E ](https://youtu.be/AYb-EUcz72U)
- **🔥 Totally free and open-source**
- **🔥 Allow playing with [Mouse & Keyboard](https://better-xcloud.github.io/mouse-and-keyboard)**
- **🔥 Enable [Remote Play](https://better-xcloud.github.io/remote-play) support**
> 1080p resolution and can stream Xbox 360 games.
- **🔥 [Improve visual quality](https://better-xcloud.github.io/ingame-features/#improve-streams-clarity) of the stream**
> Similar to (but not as good as) the "Clarity Boost" of xCloud on Edge browser. [Demo video](https://youtu.be/ZhW2choAHUs).
- **🔥 Show [Stream stats](https://better-xcloud.github.io/stream-stats)**
- **🔥 [Screenshot capture](https://better-xcloud.github.io/screenshot-capture)**
- **🔥 [Touch controller](https://better-xcloud.github.io/features/#touch-controller)**
> Enable touch controller support for all games.
- [And more...](https://better-xcloud.github.io/features/)
## Donation ## Donation
If you think this project is useful and want to support future developments, please consider making a donate via [my Ko-fi page](https://ko-fi.com/redphx). If you think this project is useful and want to support future developments, please consider making a donate via [my Ko-fi page](https://ko-fi.com/redphx).
Or you can give this project a star, that's also helpful. Or you can give this project a star, that's also helpful.

17
build.sh Executable file
View File

@@ -0,0 +1,17 @@
#!/bin/bash
build_all () {
# Clear screen
printf "\033c"
# Build all variants
bun build.ts --version $1 --variant full --pretty
bun build.ts --version $1 --variant full --meta
# bun build.ts --version $1 --variant lite
# Wait for key
read -p ">> Press Enter to build again..."
build_all $1
}
build_all $1

370
build.ts Normal file → Executable file
View File

@@ -1,18 +1,78 @@
#!/usr/bin/env bun #!/usr/bin/env bun
import { readFile } from "node:fs/promises"; import { readFile, readdir } from "node:fs/promises";
import { parseArgs } from "node:util"; import { parseArgs } from "node:util";
import { sys } from "typescript"; import { sys } from "typescript";
// @ts-ignore
import txtScriptHeader from "./src/assets/header_script.txt" with { type: "text" }; import txtScriptHeader from "./src/assets/header_script.txt" with { type: "text" };
// @ts-ignore
import txtScriptHeaderLite from "./src/assets/header_script.lite.txt" with { type: "text" };
// @ts-ignore
import txtMetaHeader from "./src/assets/header_meta.txt" with { type: "text" }; import txtMetaHeader from "./src/assets/header_meta.txt" with { type: "text" };
import { assert } from "node:console";
import { ESLint } from "eslint";
enum BuildTarget { enum BuildTarget {
ALL = 'all', ALL = 'all',
ANDROID_APP = 'android-app', ANDROID_APP = 'android-app',
MOBILE = 'mobile', MOBILE = 'mobile',
WEBOS = 'webos', WEBOS = 'webos',
} }
const postProcess = (str: string): string => { type BuildVariant = 'full' | 'lite';
const MINIFY_SYNTAX = true;
function minifySvgImports(str: string): string {
// Minify SVG imports
const svgMap = {};
str = str.replaceAll(/var ([\w_]+) = `(<svg.*?\n)`;\n\n/gsm, (match, p1, p2) => {
// Remove new lines in SVG
p2 = p2.replaceAll(/\n\s*/g, '');
svgMap[p1] = '"' + p2.trim() + '"';
return '';
});
for (const name in svgMap) {
str = str.replace(name + ',', svgMap[name] + ',');
str = str.replace(name + '\n', svgMap[name] + '\n');
}
return str;
}
function minifyCodeImports(str: string): string {
str = str.replaceAll(/var ([\w_]+_default\d?) = `(.*?)`;/gsm, (match, p1, p2) => {
// Remove new lines in SVG
p2 = p2.replaceAll(/\n\s*/g, '\n');
p2 = p2.replaceAll(/\n\/\/.*/g, '\n');
p2 = p2.replaceAll(/^\/\/.*/g, '\n');
p2 = p2.replaceAll(/\n+/g, '\n');
p2 = p2.trim();
return `var ${p1} = \`${p2}\`;`;
});
return str;
}
function minifyIfElse(str: string): string {
// Collapse if/else blocks without curly braces
return str.replaceAll(/((if \(.*?\)|else)\n\s+)/g, '$2 ');
}
function removeComments(str: string): string {
// Remove enum's inlining comments
str = str.replaceAll(/ \/\* [A-Z0-9_:]+ \*\//g, '');
str = str.replaceAll('/* @__PURE__ */ ', '');
// Remove comments from import
str = str.replaceAll(/\/\/ src.*\n/g, '');
return str;
}
function postProcess(str: string, pretty: boolean): string {
// Unescape unicode charaters // Unescape unicode charaters
str = unescape((str.replace(/\\u/g, '%u'))); str = unescape((str.replace(/\\u/g, '%u')));
// Replace \x00 to normal character // Replace \x00 to normal character
@@ -21,83 +81,269 @@ const postProcess = (str: string): string => {
// Replace "globalThis." with "var"; // Replace "globalThis." with "var";
str = str.replaceAll('globalThis.', 'var '); str = str.replaceAll('globalThis.', 'var ');
// Add ADDITIONAL CODE block str = removeComments(str);
str = str.replace('var DEFAULT_FLAGS', '\n/* ADDITIONAL CODE */\n\nvar DEFAULT_FLAGS');
// Add ADDITIONAL CODE block
str = str.replace('var DEFAULT_FLAGS', '\n/* ADDITIONAL CODE */\n\nvar DEFAULT_FLAGS');
str = str.replaceAll('(e) => `', 'e => `');
// Simplify object definitions
// {[1]: "a"} => {1: "a"}
str = str.replaceAll(/\[(\d+)\]: /g, '$1: ');
// {["a"]: 1, ["b-c"]: 2} => {a: 1, "b-c": 2}
str = str.replaceAll(/\["([^"]+)"\]: /g, function(match, p1) {
if (p1.includes('-') || p1.match(/^\d/)) {
p1 = `"${p1}"`;
}
return p1 + ': ';
});
str = minifySvgImports(str);
str = minifyCodeImports(str);
// Collapse empty brackets
str = str.replaceAll(/\{[\s\n]+\}/g, '{}');
// Remove blank lines
str = str.replaceAll(/\n([\s]*)\n/g, "\n");
// Minify WebGL shaders & JS strings
// Replace "\n " with "\n"
str = str.replaceAll(/\\n+\s*/g, '\\n');
// Remove comment line
str = str.replaceAll(/\\n\/\/.*?(?=\\n)/g, '');
// Replace ${"time".toUpperCase()} with "TIME"
// str = str.replaceAll(/\$\{"([^"]+)"\.toUpperCase\(\)\}/g, (match, p1) => {
// return p1.toUpperCase();
// });
// Replace " (e) =>" to " e =>"
// str = str.replaceAll(/ \(([^\s,.$()]+)\) =>/g, ' $1 =>');
// Set indent to 1 space
if (MINIFY_SYNTAX) {
str = minifyIfElse(str);
str = str.replaceAll(/\n(\s+|\})/g, (match, p1) => {
if (pretty) {
if (p1 === '}') {
return '\n}';
} else {
const len = p1.length / 2;
return '\n' + ' '.repeat(len);
}
} else {
return (p1 === '}') ? '}' : '';
}
});
}
// Fix unicode regex in Patcher.optimizeGameSlugGenerator
str = str.replaceAll('^\\™', '^\\\\u2122');
assert(str.includes('/* ADDITIONAL CODE */'));
assert(str.includes('window.BX_EXPOSED = BxExposed'));
assert(str.includes('window.BxEvent = BxEvent'));
assert(str.includes('window.BX_FETCH = window.fetch'));
return str; return str;
} }
const build = async (target: BuildTarget, version: string, config: any={}) => { async function buildPatches() {
console.log('-- Target:', target); const inputDir = './src/modules/patcher/patches/src';
const startTime = performance.now(); const outputDir = './src/modules/patcher/patches';
let outputScriptName = 'better-xcloud'; const files = await readdir(inputDir);
if (target !== BuildTarget.ALL) { const tsFiles = files.filter(file => file.endsWith('.ts'));
outputScriptName += `.${target}`;
}
let outputMetaName = outputScriptName;
outputScriptName += '.user.js';
outputMetaName += '.meta.js';
const outDir = './dist'; tsFiles.forEach(async file => {
// You can perform any operation with each TypeScript file
console.log(`Building patch: ${file}`);
const filePath = `${inputDir}/${file}`;
let output = await Bun.build({ await Bun.build({
entrypoints: ['src/index.ts'], entrypoints: [filePath],
outdir: outDir, outdir: outputDir,
naming: outputScriptName, target: 'browser',
define: { format: 'esm',
'Bun.env.BUILD_TARGET': JSON.stringify(target), minify: {
'Bun.env.SCRIPT_VERSION': JSON.stringify(version), syntax: true,
}, whitespace: true,
}); },
});
if (!output.success) { const outputFile = `${outputDir}/${file.replace('.ts', '.js')}`;
console.log(output);
process.exit(1);
}
const {path} = output.outputs[0]; let code = await readFile(outputFile, 'utf-8');
// Get generated file
let result = postProcess(await readFile(path, 'utf-8'));
// Replace [[VERSION]] with real value // Replace "$this$" to "this"
const scriptHeader = txtScriptHeader.replace('[[VERSION]]', version); code = code.replaceAll('$this$', 'this');
// Save to script // Minify code
await Bun.write(path, scriptHeader + result); code = removeComments(code);
console.log(`---- [${target}] done in ${performance.now() - startTime} ms`); code = minifyIfElse(code);
// Create meta file // Save
await Bun.write(outDir + '/' + outputMetaName, txtMetaHeader.replace('[[VERSION]]', version)); await Bun.write(outputFile, code);
console.log(`Patch built successfully: ${file}`)
});
}
async function build(target: BuildTarget, params: { version: string, variant: BuildVariant, pretty: boolean, meta: boolean }, config: any={}) {
const { version, variant, pretty, meta } = params;
console.log('-- Target:', target);
const startTime = performance.now();
let outputScriptName = 'better-xcloud';
if (target !== BuildTarget.ALL) {
outputScriptName += `.${target}`;
}
if (variant !== 'full') {
outputScriptName += `.${variant}`;
}
let outputMetaName = outputScriptName;
if (pretty) {
outputScriptName += '.pretty';
}
outputScriptName += '.user.js';
outputMetaName += '.meta.js';
const outDir = './dist';
await buildPatches();
let output = await Bun.build({
entrypoints: ['src/index.ts'],
outdir: outDir,
naming: outputScriptName,
minify: {
syntax: MINIFY_SYNTAX,
},
define: {
'Bun.env.BUILD_TARGET': JSON.stringify(target),
'Bun.env.BUILD_VARIANT': JSON.stringify(variant),
'Bun.env.SCRIPT_VERSION': JSON.stringify(version),
},
});
if (!output.success) {
console.log(output);
process.exit(1);
}
const {path} = output.outputs[0];
// Get generated file
let result = postProcess(await readFile(path, 'utf-8'), pretty);
// Replace [[VERSION]] with real value
let scriptHeader: string;
if (variant === 'full') {
scriptHeader = txtScriptHeader;
} else {
scriptHeader = txtScriptHeaderLite;
}
scriptHeader = scriptHeader.replace('[[VERSION]]', version);
// Save to script
await Bun.write(path, scriptHeader + result);
// Create meta file (don't build if it's beta version)
if (meta && !version.includes('beta') && variant === 'full') {
await Bun.write(outDir + '/' + outputMetaName, txtMetaHeader.replace('[[VERSION]]', version));
}
// Check with ESLint
const eslint = new ESLint();
const results = await eslint.lintFiles([path]);
results[0].messages.forEach((msg: any) => {
console.error(`${path}#${msg.line}: ${msg.message}`);
});
console.log(`---- [${target}] done in ${performance.now() - startTime} ms`);
console.log(`---- [${target}] ${new Date()}`);
} }
const buildTargets = [ const buildTargets = [
BuildTarget.ALL, BuildTarget.ALL,
// BuildTarget.ANDROID_APP, // BuildTarget.ANDROID_APP,
// BuildTarget.MOBILE, // BuildTarget.MOBILE,
// BuildTarget.WEBOS, // BuildTarget.WEBOS,
]; ];
const { values, positionals } = parseArgs({ const { values, positionals } = parseArgs({
args: Bun.argv, args: Bun.argv,
options: { options: {
version: { version: {
type: 'string', type: 'string',
},
}, variant: {
}, type: 'string',
strict: true, default: 'full',
allowPositionals: true, },
});
pretty: {
type: 'boolean',
default: false,
},
meta: {
type: 'boolean',
default: false,
},
},
strict: true,
allowPositionals: true,
}) as {
values: {
version: string,
variant: BuildVariant,
pretty: boolean,
meta: boolean,
},
positionals: string[],
};
if (!values['version']) { if (!values['version']) {
console.log('Missing --version param'); console.log('Missing --version param');
sys.exit(-1); sys.exit(-1);
} }
console.log('Building: ', values['version']); if (values['variant'] !== 'full' && values['variant'] !== 'lite') {
console.log('--variant param must be either "full" or "lite"');
const config = {}; sys.exit(-1);
for (const target of buildTargets) {
await build(target, values['version'], config);
} }
async function main() {
const config = {};
console.log(`Building: VERSION=${values['version']}, VARIANT=${values['variant']}`);
for (const target of buildTargets) {
await build(target, values, config);
}
console.log('')
// console.log('\n** Press Enter to build or Esc to exit');
}
function onKeyPress(data: any) {
const keyCode = data[0];
if (keyCode === 13) { // Enter key
main();
} else if (keyCode === 27) { // Esc key
process.exit(0);
}
}
main();
/*
process.stdin.setRawMode(true);
process.stdin.resume();
process.stdin.on('data', onKeyPress);
*/

316
bun.lock Normal file
View File

@@ -0,0 +1,316 @@
{
"lockfileVersion": 1,
"workspaces": {
"": {
"devDependencies": {
"@types/bun": "^1.2.0",
"@types/node": "^22.10.10",
"@types/stylus": "^0.48.43",
"@webgpu/types": "^0.1.53",
"eslint": "^9.19.0",
"eslint-plugin-compat": "^6.0.2",
"stylus": "^0.64.0",
},
"peerDependencies": {
"typescript": "^5.7.2",
},
},
},
"packages": {
"@adobe/css-tools": ["@adobe/css-tools@4.3.3", "", {}, "sha512-rE0Pygv0sEZ4vBWHlAgJLGDU7Pm8xoO6p3wsEceb7GYAjScrOHpEo8KK/eVkAcnSM+slAEtXjA2JpdjLp4fJQQ=="],
"@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.4.0", "", { "dependencies": { "eslint-visitor-keys": "^3.3.0" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA=="],
"@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.1", "", {}, "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ=="],
"@eslint/config-array": ["@eslint/config-array@0.19.0", "", { "dependencies": { "@eslint/object-schema": "^2.1.4", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-zdHg2FPIFNKPdcHWtiNT+jEFCHYVplAXRDlQDyqy0zGx/q2parwh7brGJSiTxRk/TSMkbM//zt/f5CHgyTyaSQ=="],
"@eslint/core": ["@eslint/core@0.11.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-DWUB2pksgNEb6Bz2fggIy1wh6fGgZP4Xyy/Mt0QZPiloKKXerbqq9D3SBQTlCRYOrcRPu4vuz+CGjwdfqxnoWA=="],
"@eslint/eslintrc": ["@eslint/eslintrc@3.2.0", "", { "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" } }, "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w=="],
"@eslint/js": ["@eslint/js@9.20.0", "", {}, "sha512-iZA07H9io9Wn836aVTytRaNqh00Sad+EamwOVJT12GTLw1VGMFV/4JaME+JjLtr9fiGaoWgYnS54wrfWsSs4oQ=="],
"@eslint/object-schema": ["@eslint/object-schema@2.1.4", "", {}, "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ=="],
"@eslint/plugin-kit": ["@eslint/plugin-kit@0.2.5", "", { "dependencies": { "@eslint/core": "^0.10.0", "levn": "^0.4.1" } }, "sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A=="],
"@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="],
"@humanfs/node": ["@humanfs/node@0.16.6", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.3.0" } }, "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw=="],
"@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="],
"@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.1", "", {}, "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA=="],
"@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="],
"@mdn/browser-compat-data": ["@mdn/browser-compat-data@5.5.42", "", {}, "sha512-qhHVgb2dxaFNT00Z1upHaDCstUEjjrgtIkrk4tr+YnDSGbTIKncbdydIpSed+RCXz0f6nb4UDD4eKEWokNom6g=="],
"@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="],
"@types/bun": ["@types/bun@1.2.2", "", { "dependencies": { "bun-types": "1.2.2" } }, "sha512-tr74gdku+AEDN5ergNiBnplr7hpDp3V1h7fqI2GcR/rsUaM39jpSeKH0TFibRvU0KwniRx5POgaYnaXbk0hU+w=="],
"@types/estree": ["@types/estree@1.0.6", "", {}, "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw=="],
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
"@types/node": ["@types/node@22.13.1", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-jK8uzQlrvXqEU91UxiK5J7pKHyzgnI1Qnl0QDHIgVGuolJhRb9EEl28Cj9b3rGR8B2lhFCtvIm5os8lFnO/1Ew=="],
"@types/stylus": ["@types/stylus@0.48.43", "", { "dependencies": { "@types/node": "*" } }, "sha512-72dv/zdhuyXWVHUXG2VTPEQdOG+oen95/DNFx2aMFFaY6LoITI6PwEqf5x31JF49kp2w9hvUzkNfTGBIeg61LQ=="],
"@types/ws": ["@types/ws@8.5.10", "", { "dependencies": { "@types/node": "*" } }, "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A=="],
"@webgpu/types": ["@webgpu/types@0.1.54", "", {}, "sha512-81oaalC8LFrXjhsczomEQ0u3jG+TqE6V9QHLA8GNZq/Rnot0KDugu3LhSYSlie8tSdooAN1Hov05asrUUp9qgg=="],
"acorn": ["acorn@8.14.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA=="],
"acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="],
"ajv": ["ajv@6.12.6", "", { "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" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="],
"ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="],
"ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
"argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
"ast-metadata-inferer": ["ast-metadata-inferer@0.8.1", "", { "dependencies": { "@mdn/browser-compat-data": "^5.6.19" } }, "sha512-ht3Dm6Zr7SXv6t1Ra6gFo0+kLDglHGrEbYihTkcycrbHw7WCcuhBzPlJYHEsIpycaUwzsJHje+vUcxXUX4ztTA=="],
"balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
"brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="],
"browserslist": ["browserslist@4.24.3", "", { "dependencies": { "caniuse-lite": "^1.0.30001688", "electron-to-chromium": "^1.5.73", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.1" }, "bin": { "browserslist": "cli.js" } }, "sha512-1CPmv8iobE2fyRMV97dAcMVegvvWKxmq94hkLiAkUGwKVTyDLw33K+ZxiFrREKmmps4rIw6grcCFCnTMSZ/YiA=="],
"bun-types": ["bun-types@1.2.2", "", { "dependencies": { "@types/node": "*", "@types/ws": "~8.5.10" } }, "sha512-RCbMH5elr9gjgDGDhkTTugA21XtJAy/9jkKe/G3WR2q17VPGhcquf9Sir6uay9iW+7P/BV0CAHA1XlHXMAVKHg=="],
"callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
"caniuse-lite": ["caniuse-lite@1.0.30001690", "", {}, "sha512-5ExiE3qQN6oF8Clf8ifIDcMRCRE/dMGcETG/XGMD8/XiXm6HXQgQTh1yZYLXXpSOsEUlJm1Xr7kGULZTuGtP/w=="],
"chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
"concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
"debug": ["debug@4.3.5", "", { "dependencies": { "ms": "2.1.2" } }, "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg=="],
"deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="],
"eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="],
"electron-to-chromium": ["electron-to-chromium@1.5.75", "", {}, "sha512-Lf3++DumRE/QmweGjU+ZcKqQ+3bKkU/qjaKYhIJKEOhgIO9Xs6IiAQFkfFoj+RhgDk4LUeNsLo6plExHqSyu6Q=="],
"emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
"escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
"escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
"eslint": ["eslint@9.20.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.19.0", "@eslint/core": "^0.11.0", "@eslint/eslintrc": "^3.2.0", "@eslint/js": "9.20.0", "@eslint/plugin-kit": "^0.2.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.1", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.2.0", "eslint-visitor-keys": "^4.2.0", "espree": "^10.3.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", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-aL4F8167Hg4IvsW89ejnpTwx+B/UQRzJPGgbIOl+4XqffWsahVVsLEWoZvnrVuwpWmnRd7XeXmQI1zlKcFDteA=="],
"eslint-plugin-compat": ["eslint-plugin-compat@6.0.2", "", { "dependencies": { "@mdn/browser-compat-data": "^5.5.35", "ast-metadata-inferer": "^0.8.1", "browserslist": "^4.24.2", "caniuse-lite": "^1.0.30001687", "find-up": "^5.0.0", "globals": "^15.7.0", "lodash.memoize": "^4.1.2", "semver": "^7.6.2" }, "peerDependencies": { "eslint": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0" } }, "sha512-1ME+YfJjmOz1blH0nPZpHgjMGK4kjgEeoYqGCqoBPQ/mGu/dJzdoP0f1C8H2jcWZjzhZjAMccbM/VdXhPORIfA=="],
"eslint-scope": ["eslint-scope@8.2.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A=="],
"eslint-visitor-keys": ["eslint-visitor-keys@4.2.0", "", {}, "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw=="],
"espree": ["espree@10.3.0", "", { "dependencies": { "acorn": "^8.14.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.0" } }, "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg=="],
"esquery": ["esquery@1.6.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="],
"esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="],
"estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="],
"esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="],
"fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
"fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="],
"fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="],
"file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="],
"find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="],
"flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="],
"flatted": ["flatted@3.3.1", "", {}, "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw=="],
"foreground-child": ["foreground-child@3.3.0", "", { "dependencies": { "cross-spawn": "^7.0.0", "signal-exit": "^4.0.1" } }, "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg=="],
"glob": ["glob@10.4.5", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg=="],
"glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
"globals": ["globals@15.8.0", "", {}, "sha512-VZAJ4cewHTExBWDHR6yptdIBlx9YSSZuwojj9Nt5mBRXQzrKakDsVKQ1J63sklLvzAJm0X5+RpO4i3Y2hcOnFw=="],
"has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
"ignore": ["ignore@5.3.1", "", {}, "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw=="],
"import-fresh": ["import-fresh@3.3.0", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw=="],
"imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="],
"is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
"is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
"is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
"jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="],
"js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="],
"json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="],
"json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="],
"json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="],
"keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="],
"levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="],
"locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
"lodash.memoize": ["lodash.memoize@4.1.2", "", {}, "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag=="],
"lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="],
"lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
"minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
"minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],
"ms": ["ms@2.1.2", "", {}, "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="],
"natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="],
"node-releases": ["node-releases@2.0.19", "", {}, "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw=="],
"optionator": ["optionator@0.9.4", "", { "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" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="],
"p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="],
"p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="],
"package-json-from-dist": ["package-json-from-dist@1.0.1", "", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="],
"parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="],
"path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
"path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
"prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
"resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
"sax": ["sax@1.4.1", "", {}, "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg=="],
"semver": ["semver@7.6.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A=="],
"shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
"signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
"source-map": ["source-map@0.7.4", "", {}, "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA=="],
"string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="],
"string-width-cjs": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
"strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="],
"strip-ansi-cjs": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
"strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="],
"stylus": ["stylus@0.64.0", "", { "dependencies": { "@adobe/css-tools": "~4.3.3", "debug": "^4.3.2", "glob": "^10.4.5", "sax": "~1.4.1", "source-map": "^0.7.3" }, "bin": { "stylus": "bin/stylus" } }, "sha512-ZIdT8eUv8tegmqy1tTIdJv9We2DumkNZFdCF5mz/Kpq3OcTaxSuCAYZge6HKK2CmNC02G1eJig2RV7XTw5hQrA=="],
"supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
"type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="],
"typescript": ["typescript@5.7.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg=="],
"undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
"update-browserslist-db": ["update-browserslist-db@1.1.1", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.0" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A=="],
"uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
"word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="],
"wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="],
"wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
"yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
"@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
"@eslint/eslintrc/globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="],
"@eslint/plugin-kit/@eslint/core": ["@eslint/core@0.10.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw=="],
"@humanfs/node/@humanwhocodes/retry": ["@humanwhocodes/retry@0.3.1", "", {}, "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA=="],
"@types/stylus/@types/node": ["@types/node@22.5.5", "", { "dependencies": { "undici-types": "~6.19.2" } }, "sha512-Xjs4y5UPO/CLdzpgR6GirZJx36yScjh73+2NlLlkFRSoQN8B0DpfXPdZGnvVmLRLOsqDpOfTNv7D9trgGhmOIA=="],
"@types/ws/@types/node": ["@types/node@20.14.2", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-xyu6WAMVwv6AKFLB+e/7ySZVr/0zLCzOa7rSpq6jNwpqOrUbcACDWC+53d4n2QHOnDou0fbIsg8wZu/sxrnI4Q=="],
"ast-metadata-inferer/@mdn/browser-compat-data": ["@mdn/browser-compat-data@5.6.26", "", {}, "sha512-7NdgdOR7lkzrN70zGSULmrcvKyi/aJjpTJRCbuy8IZuHiLkPTvsr10jW0MJgWzK2l2wTmhdQvegTw6yNU5AVNQ=="],
"foreground-child/cross-spawn": ["cross-spawn@7.0.3", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w=="],
"glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
"string-width-cjs/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
"string-width-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
"strip-ansi-cjs/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
"wrap-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="],
"wrap-ansi-cjs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
"wrap-ansi-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
"@types/stylus/@types/node/undici-types": ["undici-types@6.19.8", "", {}, "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="],
"@types/ws/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="],
"glob/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
"string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
"wrap-ansi-cjs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
"wrap-ansi-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
}
}

View File

@@ -1,5 +1,5 @@
// ==UserScript== // ==UserScript==
// @name Better xCloud // @name Better xCloud
// @namespace https://github.com/redphx // @namespace https://github.com/redphx
// @version 4.6.0 // @version 6.4.2
// ==/UserScript== // ==/UserScript==

10346
dist/better-xcloud.pretty.user.js vendored Normal file

File diff suppressed because one or more lines are too long

9184
dist/better-xcloud.user.js vendored Normal file → Executable file

File diff suppressed because one or more lines are too long

3
eslint.config.mjs Executable file
View File

@@ -0,0 +1,3 @@
import compat from "eslint-plugin-compat";
export default [compat.configs['flat/recommended']];

17
package.json Normal file → Executable file
View File

@@ -2,16 +2,23 @@
"name": "better-xcloud", "name": "better-xcloud",
"module": "src/index.ts", "module": "src/index.ts",
"type": "module", "type": "module",
"sideEffects": false,
"browserslist": [
"Chrome >= 80"
],
"bin": { "bin": {
"build": "build.ts" "build": "build.ts"
}, },
"devDependencies": { "devDependencies": {
"@types/bun": "latest", "@types/bun": "^1.2.2",
"@types/node": "^20.12.7", "@types/node": "^22.13.1",
"@types/stylus": "^0.48.42", "@types/stylus": "^0.48.43",
"stylus": "^0.63.0" "@webgpu/types": "^0.1.54",
"eslint": "^9.20.0",
"eslint-plugin-compat": "^6.0.2",
"stylus": "^0.64.0"
}, },
"peerDependencies": { "peerDependencies": {
"typescript": "^5.0.0" "typescript": "^5.7.2"
} }
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

45
scripts/custom-flags.user.js Executable file
View File

@@ -0,0 +1,45 @@
// ==UserScript==
// @name Better xCloud - Custom flags
// @namespace https://github.com/redphx
// @version 1.0.0
// @description Customize Better xCloud script
// @author redphx
// @license MIT
// @match https://www.xbox.com/*/play*
// @run-at document-start
// @grant none
// ==/UserScript==
'use strict';
/*
Make sure this script is being loaded before the Better xCloud script.
How to:
1. Uninstall Better xCloud script.
2. Install this script.
3. Reinstall Better xCloud script. All your settings are still there.
*/
// Change this to `false` if you want to temporary disable the script
const enabled = true;
enabled && (window.BX_FLAGS = {
// Toggle WebGPU Renderer
// https://github.com/redphx/better-xcloud/discussions/657
EnableWebGPURenderer: false,
/*
Add titleId of the game(s) you want to test native M&KB support here.
Keep in mind: this method only works with some games.
Example:
- Flight Simulator has this link: /play/games/microsoft-flight-simulator-standard-40th-anniversa/9PMQDM08SNK9
- That means its titleId is "9PMQDM08SNK9"
- So it becomes:
ForceNativeMkbTitles: [
"9PMQDM08SNK9",
],
*/
ForceNativeMkbTitles: [
],
});

175
src/assets/css/button.styl Normal file → Executable file
View File

@@ -1,5 +1,10 @@
.bx-button { .bx-button {
background-color: var(--bx-default-button-color); --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: unquote('rgb(var(--button-rgb))');
user-select: none; user-select: none;
-webkit-user-select: none; -webkit-user-select: none;
color: #fff; color: #fff;
@@ -14,88 +19,188 @@
cursor: pointer; cursor: pointer;
overflow: hidden; overflow: hidden;
&:not([disabled]):active {
background-color: unquote('rgb(var(--button-active-rgb))');
}
&:focus { &:focus {
outline: none !important; outline: none !important;
} }
&:hover, &.bx-focusable:focus { &:not([disabled]):not(:active) {
background-color: var(--bx-default-button-hover-color); &:hover, &.bx-focusable:focus {
background-color: unquote('rgb(var(--button-hover-rgb))');
}
} }
&:disabled { &:disabled {
cursor: default; cursor: default;
background-color: var(--bx-default-button-disabled-color); background-color: unquote('rgb(var(--button-disabled-rgb))');
opacity: 0.5;
} }
&.bx-ghost { &.bx-ghost {
background-color: transparent; background-color: transparent;
&:hover, &.bx-focusable:focus { &:not([disabled]):not(:active) {
background-color: var(--bx-default-button-hover-color); &:hover, &.bx-focusable:focus {
background-color: unquote('rgb(var(--button-hover-rgb))');
}
} }
} }
&.bx-primary { &.bx-primary {
background-color: var(--bx-primary-button-color); --button-rgb: var(--bx-primary-button-rgb);
&:hover, &.bx-focusable:focus { &:not([disabled]):active {
background-color: var(--bx-primary-button-hover-color); --button-active-rgb: var(--bx-primary-button-active-rgb);
}
&:not([disabled]):not(:active) {
&:hover, &.bx-focusable:focus {
--button-hover-rgb: var(--bx-primary-button-hover-rgb);
}
} }
&:disabled { &:disabled {
background-color: var(--bx-primary-button-disabled-color); --button-disabled-rgb: var(--bx-primary-button-disabled-rgb);
}
}
&.bx-warning {
--button-rgb: var(--bx-warning-button-rgb);
&:not([disabled]):active {
--button-active-rgb: var(--bx-warning-button-active-rgb);
}
&:not([disabled]):not(:active) {
&:hover, &.bx-focusable:focus {
--button-hover-rgb: var(--bx-warning-button-hover-rgb);
}
}
&:disabled {
--button-disabled-rgb: var(--bx-warning-button-disabled-rgb);
} }
} }
&.bx-danger { &.bx-danger {
background-color: var(--bx-danger-button-color); --button-rgb: var(--bx-danger-button-rgb);
&:hover, &.bx-focusable:focus { &:not([disabled]):active {
background-color: var(--bx-danger-button-hover-color); --button-active-rgb: var(--bx-danger-button-active-rgb);
}
&:not([disabled]):not(:active) {
&:hover, &.bx-focusable:focus {
--button-hover-rgb: var(--bx-danger-button-hover-rgb);
}
} }
&:disabled { &:disabled {
background-color: var(--bx-danger-button-disabled-color); --button-disabled-rgb: var(--bx-danger-button-disabled-rgb);
} }
} }
&.bx-frosted {
--button-alpha: 0.2;
background-color: unquote('rgba(var(--button-rgb), var(--button-alpha))');
&:not([disabled]):not(:active) {
&:hover, &.bx-focusable:focus {
background-color: unquote('rgba(var(--button-hover-rgb), var(--button-alpha))');
}
}
}
&.bx-drop-shadow {
box-shadow: 0 0 4px #00000080;
}
&.bx-tall {
height: calc(var(--bx-button-height) * 1.5) !important;
}
&.bx-circular {
border-radius: var(--bx-button-height);
width: var(--bx-button-height);
height: var(--bx-button-height);
}
svg { svg {
display: inline-block; display: inline-block;
width: 16px; width: 16px;
height: var(--bx-button-height); height: var(--bx-button-height);
&:not(:only-child) {
margin-right: 4px;
}
} }
span { span {
display: inline-block; display: inline-block;
height: var(--bx-button-height); /* height: var(--bx-button-height); */
line-height: var(--bx-button-height); line-height: var(--bx-button-height);
vertical-align: middle; vertical-align: middle;
vertical-align: -webkit-baseline-middle; /* vertical-align: -webkit-baseline-middle; */
color: #fff; color: #fff;
overflow: hidden; overflow: hidden;
white-space: nowrap; white-space: nowrap;
// Text with icon
&:not(:only-child) {
margin-inline-start: 8px;
}
} }
&.bx-focusable { &.bx-button-multi-lines {
position: relative; height: auto;
text-align: left;
padding: 10px;
&::after { span {
border: 2px solid transparent; line-height: unset;
border-radius: 4px; display: block;
&:last-of-type {
text-transform: none;
font-weight: normal;
font-family: "Segoe Sans Variable Text";
font-size: 12px;
margin-top: 4px;
}
} }
}
}
.bx-focusable {
position: relative;
overflow: visible;
&::after {
border: 2px solid transparent;
border-radius: 10px;
}
&:focus::after {
offset = -6px;
content: '';
border-color: white;
position: absolute;
top: offset;
left: offset;
right: offset;
bottom: offset;
}
html[data-active-input=touch] &,
html[data-active-input=mouse] & {
&:focus::after { &:focus::after {
content: ''; border-color: transparent !important;
border-color: white; }
position: absolute; }
top: 0;
left: 0; &.bx-circular {
right: 0; &::after {
bottom: 0; border-radius: var(--bx-button-height);
} }
} }
} }
@@ -107,3 +212,9 @@ a.bx-button {
text-align: center; text-align: center;
} }
} }
button.bx-inactive {
pointer-events: none;
opacity: 0.2;
background: transparent !important;
}

View File

@@ -0,0 +1,89 @@
.bx-controller-customizations-container {
.bx-btn-detect {
display: block;
margin-bottom: 20px;
&.bx-monospaced {
background: none;
font-weight: bold;
font-size: 12px;
}
}
.bx-buttons-grid {
display: grid;
grid-template-columns: auto auto;
column-gap: 20px;
row-gap: 10px;
margin-bottom: 20px;
}
}
.bx-controller-key-row {
display: flex;
align-items: stretch;
> label {
margin-bottom: 0;
font-family: var(--bx-promptfont-font);
font-size: 32px;
text-align: center;
min-width: 50px;
flex-shrink: 0;
display: flex;
align-self: center;
&::after {
content: '';
margin: 0 12px;
font-size: 16px;
align-self: center;
}
}
.bx-select {
width: 100% !important;
> div {
min-width: 50px;
}
label {
font-family: var(--bx-promptfont-font), var(--bx-normal-font);
font-size: 32px;
text-align: center;
margin-bottom: 6px;
height: 40px;
line-height: 40px;
}
}
&:hover {
> label {
color: #ffe64b;
&::after {
color: #fff;
}
}
}
}
.bx-controller-customization-summary {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 8px;
margin-top: 10px;
span {
font-family: var(--bx-promptfont);
font-size: 24px;
border-radius: 6px;
background: #131313;
color: #fff;
display: inline-block;
padding: 2px;
text-align: center;
}
}

10
src/assets/css/game-bar.styl Normal file → Executable file
View File

@@ -76,21 +76,21 @@
} }
/* Touch controller buttons */ /* Touch controller buttons */
div[data-enabled] { div[data-activated] {
button { button {
display: none; display: none;
} }
} }
/* Show enabled button */ /* Show default button */
div[data-enabled='true'] { div[data-activated='false'] {
button:first-of-type { button:first-of-type {
display: block; display: block;
} }
} }
/* Show enable button */ /* Show activated button */
div[data-enabled='false'] { div[data-activated='true'] {
button:last-of-type { button:last-of-type {
display: block; display: block;
} }

View File

@@ -1,184 +0,0 @@
.bx-settings-reload-button {
margin-top: 10px;
height: calc(var(--bx-button-height) * 1.5);
}
.bx-settings-container {
background-color: #151515;
user-select: none;
-webkit-user-select: none;
color: #fff;
font-family: var(--bx-normal-font);
}
@media (hover: hover) {
.bx-settings-wrapper a.bx-settings-title:hover {
color: #83f73a;
}
}
.bx-settings-wrapper {
width: 450px;
margin: auto;
padding: 12px 6px;
@media screen and (max-width: 450px) {
width: 100%;
}
*:focus {
outline: none !important;
}
.bx-settings-title-wrapper {
display: flex;
margin-bottom: 10px;
align-items: center;
}
a.bx-settings-title {
font-family: var(--bx-title-font);
font-size: 1.4rem;
text-decoration: none;
font-weight: bold;
display: block;
color: #5dc21e;
flex: 1;
&:focus {
color: #83f73a;
}
}
.bx-button.bx-primary {
margin-top: 8px;
}
a.bx-settings-update {
display: block;
color: #ff834b;
text-decoration: none;
margin-bottom: 8px;
text-align: center;
background: #222;
border-radius: 4px;
padding: 4px;
&:hover {
@media (hover: hover) {
color: #ff9869;
text-decoration: underline;
}
}
&:focus {
color: #ff9869;
text-decoration: underline;
}
}
}
.bx-settings-group-label {
font-weight: bold;
display: block;
font-size: 1.1rem;
}
.bx-settings-row {
display: flex;
padding: 6px 12px;
position: relative;
label {
flex: 1;
align-self: center;
margin-bottom: 0;
}
&: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;
}
}
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;
}
}
}
.bx-settings-group-label b, .bx-settings-row label b {
display: block;
font-size: 12px;
font-style: italic;
font-weight: normal;
color: #828282;
}
.bx-settings-group-label b {
margin-bottom: 8px;
}
.bx-settings-app-version {
margin-top: 10px;
text-align: center;
color: #747474;
font-size: 12px;
}
.bx-donation-link {
display: block;
text-align: center;
text-decoration: none;
height: 20px;
line-height: 20px;
font-size: 14px;
margin-top: 10px;
color: #5dc21e;
&:hover {
color: #6dd72b;
}
&:focus {
text-decoration: underline;
}
}
.bx-settings-custom-user-agent {
display: block;
width: 100%;
}

65
src/assets/css/guide-menu.styl Executable file
View File

@@ -0,0 +1,65 @@
.bx-guide-home-achievements-progress {
display: flex;
gap: 10px;
flex-direction: row;
.bx-button {
margin-bottom: 0 !important;
}
body[data-bx-media-type=tv] & {
flex-direction: column;
}
body:not([data-bx-media-type=tv]) & {
flex-direction: row;
> button:first-of-type {
flex: 1;
}
> button:last-of-type {
width: 40px;
span {
display: none;
}
}
}
}
.bx-guide-home-buttons {
> div {
display: flex;
flex-direction: row;
gap: 12px;
body[data-bx-media-type=tv] & {
flex-direction: column;
button {
margin-bottom: 0 !important;
}
}
body:not([data-bx-media-type=tv]) & {
button {
span {
display: none;
}
}
}
}
&[data-is-playing="true"] {
button[data-state='normal'] {
display: none;
}
}
&[data-is-playing="false"] {
button[data-state='playing'] {
display: none;
}
}
}

2
src/assets/css/header.styl Normal file → Executable file
View File

@@ -4,7 +4,7 @@
svg { svg {
width: 24px; width: 24px;
height: 46px; height: 24px;
} }
} }

View File

@@ -1,12 +1,12 @@
.bx-dialog-overlay { .bx-key-binding-dialog-overlay {
position: fixed; position: fixed;
inset: 0; inset: 0;
z-index: var(--bx-dialog-overlay-z-index); z-index: var(--bx-key-binding-dialog-overlay-z-index);
background: black; background: black;
opacity: 50%; opacity: 50%;
} }
.bx-dialog { .bx-key-binding-dialog {
display: flex; display: flex;
flex-flow: column; flex-flow: column;
max-height: 90vh; max-height: 90vh;
@@ -16,9 +16,9 @@
margin-right: -50%; margin-right: -50%;
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
min-width: 420px; min-width: 420px;
padding: 20px; padding: 16px;
border-radius: 8px; border-radius: 8px;
z-index: var(--bx-dialog-z-index); z-index: var(--bx-key-binding-dialog-z-index);
background: #1a1b1e; background: #1a1b1e;
color: #fff; color: #fff;
font-weight: 400; font-weight: 400;
@@ -33,26 +33,13 @@
} }
h2 { h2 {
display: flex;
margin-bottom: 12px; margin-bottom: 12px;
color: #fff;
b { display: block;
flex: 1; font-family: var(--bx-title-font);
color: #fff; font-size: 32px;
display: block; font-weight: 400;
font-family: var(--bx-title-font); line-height: var(--bx-button-height);
font-size: 26px;
font-weight: 400;
line-height: var(--bx-button-height);
}
}
&.bx-binding-dialog {
h2 {
b {
font-family: var(--bx-promptfont-font) !important;
}
}
} }
> div { > div {
@@ -85,11 +72,26 @@
background-color: #515863; background-color: #515863;
} }
} }
ul {
margin-bottom: 1rem;
li {
display: none;
}
&[data-flags*="[1]"] > li[data-flag="1"],
&[data-flags*="[2]"] > li[data-flag="2"],
&[data-flags*="[4]"] > li[data-flag="4"],
&[data-flags*="[8]"] > li[data-flag="8"] {
display: list-item;
}
}
} }
@media screen and (max-width: 450px) { @media screen and (max-width: 450px) {
.bx-dialog { .bx-key-binding-dialog {
min-width: 100%; min-width: 100%;
} }
} }

0
src/assets/css/loading-screen.styl Normal file → Executable file
View File

30
src/assets/css/misc.styl Executable file
View File

@@ -0,0 +1,30 @@
.bx-product-details-icons {
padding: 8px;
border-radius: 4px;
svg {
margin-right: 8px;
}
}
.bx-product-details-buttons {
display: flex;
gap: 10px;
flex-direction: row;
button {
max-width: max-content;
margin: 10px 0 0 0;
display: flex;
}
}
@media (min-width: 568px) and (max-height: 480px) {
.bx-product-details-buttons {
flex-direction: column;
button {
margin: 8px 0 0 10px;
}
}
}

160
src/assets/css/mkb.styl Normal file → Executable file
View File

@@ -4,31 +4,19 @@
flex: 1; flex: 1;
padding-bottom: 10px; padding-bottom: 10px;
overflow: hidden; overflow: hidden;
select:disabled {
-webkit-appearance: none;
background: transparent;
text-align-last: right;
text-align: right;
border: none;
color: #fff;
}
} }
.bx-mkb-pointer-lock-msg { .bx-mkb-pointer-lock-msg {
display: flex;
cursor: pointer;
user-select: none; user-select: none;
-webkit-user-select: none; -webkit-user-select: none;
position: fixed; position: fixed;
left: 50%; left: 50%;
top: 50%; bottom: 40px;
transform: translateX(-50%) translateY(-50%); transform: translateX(-50%);
margin: auto; margin: auto;
background: #000000b3; background: #151515;
z-index: var(--bx-mkb-pointer-lock-msg-z-index); z-index: var(--bx-mkb-pointer-lock-msg-z-index);
color: #fff; color: #fff;
text-align: center;
font-weight: 400; font-weight: 400;
font-family: "Segoe UI", Arial, Helvetica, sans-serif; font-family: "Segoe UI", Arial, Helvetica, sans-serif;
font-size: 1.3rem; font-size: 1.3rem;
@@ -36,91 +24,55 @@
border-radius: 8px; border-radius: 8px;
align-items: center; align-items: center;
box-shadow: 0 0 6px #000; box-shadow: 0 0 6px #000;
min-width: 300px;
opacity: 0.9;
display: flex;
flex-direction: column;
gap: 10px;
&:hover { &:hover {
background: #151515; opacity: 1;
} }
button { > p {
margin-right: 12px; margin: 0;
height: 60px; width: 100%;
} font-size: 22px;
margin-bottom: 4px;
svg { font-weight: bold;
width: 32px;
}
div {
display: flex;
flex-direction: column;
text-align: left; text-align: left;
} }
p { > div {
margin: 0; width: 100%;
display: flex;
flex-direction: row;
&:first-child { gap: 10px;
font-size: 22px; button {
margin-bottom: 8px; &:first-of-type {
} flex-shrink: 1;
}
&:last-child { &:last-of-type {
font-size: 14px; flex-grow: 1;
font-style: italic; }
} }
} }
} }
.bx-mkb-preset-tools {
display: flex;
margin-bottom: 12px;
select {
flex: 1;
}
button {
margin-left: 6px;
}
}
.bx-mkb-settings-rows {
flex: 1;
overflow: scroll;
}
.bx-mkb-key-row { .bx-mkb-key-row {
display: flex; display: flex;
margin-bottom: 10px; margin-bottom: 10px;
align-items: center; align-items: center;
gap: 20px;
label { label {
margin-bottom: 0; margin-bottom: 0;
font-family: var(--bx-promptfont-font); font-family: var(--bx-promptfont-font);
font-size: 26px; font-size: 32px;
text-align: center; text-align: center;
width: 26px;
height: 32px;
line-height: 32px;
}
button {
flex: 1;
height: 32px;
line-height: 32px;
margin: 0 0 0 10px;
background: transparent;
border: none;
color: white;
border-radius: 0;
border-left: 1px solid #373737;
&:hover {
background: transparent;
cursor: default;
}
} }
} }
@@ -157,10 +109,58 @@
.bx-mkb-note { .bx-mkb-note {
display: block; display: block;
margin: 16px 0 10px; margin: 0 0 10px;
font-size: 12px; font-size: 12px;
text-align: center;
}
&:first-of-type { button.bx-binding-button {
margin-top: 0; flex: 1;
min-height: 38px;
border: none;
border-radius: 4px;
font-size: 14px;
color: #fff;
display: flex;
align-items: center;
align-self: center;
padding: 0 6px;
&:disabled {
background: #131416;
padding: 0 8px;
}
&:not(:disabled) {
border: 2px solid transparent;
border-top: none;
border-bottom: 4px solid #252525;
background: #3b3b3b;
cursor: pointer;
&:hover, &.bx-focusable:focus {
background: #20b217;
border-bottom-color: #186c13;
}
&:active {
background: #16900f;
border-bottom: 3px solid #0c4e08;
border-left-width: 2px;
border-right-width: 2px;
}
&.bx-focusable:focus {
&::after {
top: -6px;
left: -8px;
right: -8px;
bottom: -10px;
}
}
}
.bx-settings-row .bx-binding-button-wrapper & {
min-width: 60px;
} }
} }

View File

@@ -0,0 +1,217 @@
.bx-navigation-dialog {
position: absolute;
z-index: var(--bx-navigation-dialog-z-index);
font-family: var(--bx-title-font);
*:focus {
outline: none !important;
}
select:disabled {
-webkit-appearance: none;
text-align-last: right;
text-align: right;
color: #fff;
background: #131416;
border: none;
border-radius: 4px;
padding: 0 5px;
}
.bx-focusable {
&::after {
border-radius: 4px;
}
&:focus::after {
offset = 0;
top: offset;
left: offset;
right: offset;
bottom: offset;
}
}
}
.bx-navigation-dialog-overlay {
position: fixed;
background: #0b0b0be3;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: var(--bx-navigation-dialog-overlay-z-index);
&[data-is-playing="true"] {
background: transparent;
}
}
.bx-centered-dialog {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: white;
background: #1a1b1e;
border-radius: 10px;
min-width: @css{ min(calc(100vw - 20px), 500px) };
max-width: calc(100vw - 20px);
margin: 0 0 0 auto;
padding: 16px;
max-height: 95vh;
flex-direction: column;
overflow: hidden;
display: flex;
flex-direction: column;
.bx-dialog-title {
display: flex;
flex-direction: row;
align-items: center;
margin-bottom: 10px;
p {
padding: 0;
margin: 0;
flex: 1;
font-size: 1.5rem;
font-weight: bold;
}
button {
flex-shrink: 0;
}
}
.bx-dialog-content {
flex: 1;
padding: 6px;
overflow: auto;
overflow-x: hidden;
}
.bx-dialog-preset-tools {
display: flex;
margin-bottom: 12px;
gap: 6px;
button {
align-self: center;
min-height: 50px;
}
}
.bx-default-preset-note {
font-size: 12px;
font-style: italic;
text-align: center;
margin-bottom: 10px;
}
}
.bx-centered-dialog,
.bx-settings-dialog {
input {
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 option:disabled {
display: none;
}
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);
}
}
a {
color: #1c9d1c;
text-decoration: none;
&:hover, &:focus {
color: #5dc21e;
}
}
label {
margin: 0;
}
}
.bx-controller-shortcuts-manager-container {
.bx-shortcut-note {
margin-top: 10px;
font-size: 14px;
text-align: center;
}
.bx-shortcut-row {
display: flex;
gap: 10px;
margin-bottom: 10px;
align-items: center;
label.bx-prompt {
flex-shrink: 0;
font-size: 32px;
margin: 0;
&::first-letter {
letter-spacing: 6px;
}
}
}
select:disabled {
text-align: left;
text-align-last: left;
}
}
.bx-keyboard-shortcuts-manager-container {
display: flex;
flex-direction: column;
gap: 16px;
fieldset {
background: #2a2a2a;
border: 1px solid #2a2a2a;
border-radius: 4px;
padding: 4px;
}
legend {
width: auto;
padding: 4px 8px;
margin: 0 4px 4px;
background: #004f87;
box-shadow: 0px 2px 0px #071e3d;
border-radius: 4px;
font-size: 14px;
font-weight: bold;
text-transform: uppercase;
}
.bx-settings-row {
background: none;
padding: 10px;
}
}

165
src/assets/css/number-stepper.styl Normal file → Executable file
View File

@@ -1,50 +1,153 @@
.bx-number-stepper { .bx-number-stepper {
text-align: center; text-align: center;
span { > div {
display: inline-block; display: flex;
min-width: 40px; align-items: center;
font-family: var(--bx-monospaced-font);
font-size: 14px;
}
button { span {
border: none; flex: 1;
width: 24px; display: inline-block;
height: 24px; min-width: 40px;
margin: 0 4px; font-family: var(--bx-monospaced-font);
line-height: 24px; white-space: pre;
background-color: var(--bx-default-button-color); font-size: 13px;
color: #fff; margin: 0 4px;
border-radius: 4px; }
font-weight: bold;
font-size: 14px;
font-family: var(--bx-monospaced-font);
color: #fff;
&:hover { button {
@media (hover: hover) { flex-shrink: 0;
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);
&:hover {
@media (hover: hover) {
background-color: var(--bx-default-button-hover-color);
}
}
&:active {
background-color: var(--bx-default-button-hover-color); background-color: var(--bx-default-button-hover-color);
} }
}
&:active { &:disabled + span {
background-color: var(--bx-default-button-hover-color); font-family: var(--bx-title-font);
} }
&:disabled + span {
font-family: var(--bx-title-font);
} }
} }
input[type="range"] { input[type=range] {
display: block; display: block;
margin: 12px auto 2px; margin: 8px 0 2px auto;
width: 180px; min-width: 180px;
width: 100%;
color: #959595 !important; color: #959595 !important;
} }
input[type=range]:disabled, button:disabled { input[type=range]:disabled, button:disabled {
display: none; display: none;
} }
&[data-disabled=true], &[disabled=true] {
input[type=range], button {
display: none;
}
}
}
.bx-dual-number-stepper {
> span {
display: block;
font-family: var(--bx-monospaced-font);
font-size: 13px;
white-space: pre;
margin: 0 4px;
text-align: center;
}
> div {
input[type=range] {
display: block;
width: 100%;
min-width: 180px;
background: transparent;
color: #959595 !important;
appearance: none;
padding: 8px 0;
range-track() {
background: linear-gradient(90deg, #fff var(--from), var(--bx-primary-button-color) var(--from) var(--to), #fff var(--to) 100%);
height: 8px;
border-radius: 2px;
}
range-track-hover() {
background: linear-gradient(90deg, #fff var(--from), #006635 var(--from) var(--to), #fff var(--to) 100%);
}
thumb() {
margin-top: -4px;
appearance: none;
width: 4px;
height: 16px;
background: #00b85f;
border: none;
border-radius: 2px;
}
thumb-hover() {
background: #fb3232;
}
&::-webkit-slider-runnable-track {
range-track()
}
&::-moz-range-track {
range-track()
}
&::-webkit-slider-thumb {
thumb();
}
&::-moz-range-thumb {
thumb();
}
&:hover, &&:active, &:focus {
&::-webkit-slider-runnable-track {
range-track-hover();
}
&::-moz-range-track {
range-track-hover();
}
&::-webkit-slider-thumb {
thumb-hover();
}
&::-moz-range-thumb {
thumb-hover();
}
}
}
}
&[data-disabled=true], &[disabled=true] {
input[type=range] {
display: none;
}
}
} }

65
src/assets/css/remote-play.styl Normal file → Executable file
View File

@@ -1,43 +1,3 @@
.bx-remote-play-popup {
width: 100%;
max-width: 1920px;
margin: auto;
position: relative;
height: 0.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: #00000080 0px 0px 12px 0px;
@media (min-width:480px) and (min-height:calc(480px + 1px)) {
right: calc(env(safe-area-inset-right, 0px) + 32px);
}
@media (min-width:768px) and (min-height:calc(480px + 1px)) {
right: calc(env(safe-area-inset-right, 0px) + 48px);
}
@media (min-width:1920px) and (min-height:calc(480px + 1px)) {
right: calc(env(safe-area-inset-right, 0px) + 80px);
}
> .bx-button {
display: table;
margin: 0 0 0 auto;
}
}
.bx-remote-play-settings { .bx-remote-play-settings {
margin-bottom: 12px; margin-bottom: 12px;
padding-bottom: 12px; padding-bottom: 12px;
@@ -49,6 +9,7 @@
label { label {
flex: 1; flex: 1;
font-size: 14px;
p { p {
margin: 4px 0 0; margin: 4px 0 0;
@@ -57,14 +18,6 @@
font-size: 12px; font-size: 12px;
} }
} }
span {
font-weight: bold;
font-size: 18px;
display: block;
margin-bottom: 8px;
text-align: center;
}
} }
.bx-remote-play-resolution { .bx-remote-play-resolution {
@@ -91,33 +44,39 @@
.bx-remote-play-device-info { .bx-remote-play-device-info {
flex: 1; flex: 1;
align-self: center;
padding: 4px 0; padding: 4px 0;
} }
.bx-remote-play-device-name { .bx-remote-play-device-name {
font-size: 20px; font-size: 14px;
font-weight: bold; font-weight: bold;
display: inline-block; display: inline-block;
vertical-align: middle; vertical-align: middle;
} }
.bx-remote-play-console-type { .bx-remote-play-console-type {
font-size: 12px; font-size: 8px;
background: #004c87; background: #004c87;
color: #fff; color: #fff;
display: inline-block; display: inline-block;
border-radius: 14px; border-radius: 8px;
padding: 2px 10px; padding: 2px 6px;
margin-left: 8px; margin-left: 8px;
vertical-align: middle; vertical-align: middle;
} }
.bx-remote-play-power-state { .bx-remote-play-power-state {
color: #888; color: #888;
font-size: 14px; font-size: 12px;
} }
.bx-remote-play-connect-button { .bx-remote-play-connect-button {
min-height: 100%; min-height: 100%;
margin: 4px 0; margin: 4px 0;
} }
.bx-remote-play-buttons {
display: flex;
justify-content: space-between;
}

242
src/assets/css/root.styl Normal file → Executable file
View File

@@ -1,3 +1,18 @@
button_color(name, normal, hover, active, disabled)
prefix = unquote('--bx-' + name + '-button');
{prefix + '-color'}: normal;
{prefix + '-rgb'}: red(normal), green(normal), blue(normal);
{prefix + '-hover-color'}: hover;
{prefix + '-hover-rgb'}: red(hover), green(hover), blue(hover);
{prefix + '-active-color'}: active;
{prefix + '-active-rgb'}: red(active), green(active), blue(active);
{prefix + '-disabled-color'}: disabled;
{prefix + '-disabled-rgb'}: red(disabled), green(disabled), blue(disabled);
:root { :root {
--bx-title-font: Bahnschrift, Arial, Helvetica, sans-serif; --bx-title-font: Bahnschrift, Arial, Helvetica, sans-serif;
--bx-title-font-semibold: Bahnschrift Semibold, Arial, Helvetica, sans-serif; --bx-title-font-semibold: Bahnschrift Semibold, Arial, Helvetica, sans-serif;
@@ -5,39 +20,38 @@
--bx-monospaced-font: Consolas, "Courier New", Courier, monospace; --bx-monospaced-font: Consolas, "Courier New", Courier, monospace;
--bx-promptfont-font: promptfont; --bx-promptfont-font: promptfont;
--bx-button-height: 36px; --bx-button-height: 40px;
--bx-default-button-color: #2d3036; button_color('default', #2d3036, #515863, #222428, #8e8e8e);
--bx-default-button-hover-color: #515863; button_color('primary', #008746, #04b358, #044e2a, #448262);
--bx-default-button-disabled-color: #8e8e8e; button_color('warning', #c16e04, #fa9005, #965603, #a2816c);
button_color('danger', #c10404, #e61d1d, #a26c6c, #bd8282);
--bx-primary-button-color: #008746; --bx-fullscreen-text-z-index: 9999;
--bx-primary-button-hover-color: #04b358; --bx-toast-z-index: 6000;
--bx-primary-button-disabled-color: #448262; --bx-key-binding-dialog-z-index: 5010;
--bx-key-binding-dialog-overlay-z-index: 5000;
--bx-danger-button-color: #c10404; --bx-stats-bar-z-index: 4010;
--bx-danger-button-hover-color: #e61d1d;
--bx-danger-button-disabled-color: #a26c6c;
--bx-toast-z-index: 9999; --bx-navigation-dialog-z-index: 3010;
--bx-dialog-z-index: 9101; --bx-navigation-dialog-overlay-z-index: 3000;
--bx-dialog-overlay-z-index: 9100;
--bx-remote-play-popup-z-index: 9090; --bx-mkb-pointer-lock-msg-z-index: 2000;
--bx-stats-bar-z-index: 9001;
--bx-stream-settings-z-index: 9000; --bx-game-bar-z-index: 1000;
--bx-mkb-pointer-lock-msg-z-index: 8999; --bx-screenshot-animation-z-index: 200;
--bx-game-bar-z-index: 8888;
--bx-wait-time-box-z-index: 100; --bx-wait-time-box-z-index: 100;
--bx-screenshot-animation-z-index: 1;
} }
@font-face { @font-face {
font-family: 'promptfont'; font-family: 'promptfont';
src: url('https://redphx.github.io/better-xcloud/fonts/promptfont.otf'); src: url('https://redphx.github.io/better-xcloud/fonts/promptfont.otf');
unicode-range: U+2196-E011, U+27F6, U+FF31;
} }
/* Fix Stream menu buttons not hiding */ /* Fix Stream menu buttons not hiding */
div[class^=HUDButton-module__hiddenContainer] ~ div:not([class^=HUDButton-module__hiddenContainer]) { #StreamHud div[class^=HUDButton-module__hiddenContainer] ~ div:not([class^=HUDButton-module__hiddenContainer]) {
opacity: 0; opacity: 0;
pointer-events: none !important; pointer-events: none !important;
position: absolute; position: absolute;
@@ -46,7 +60,7 @@ div[class^=HUDButton-module__hiddenContainer] ~ div:not([class^=HUDButton-module
} }
/* Remove the "Cloud Gaming" text in header when the screen is too small */ /* Remove the "Cloud Gaming" text in header when the screen is too small */
@media screen and (max-width: 600px) { @media screen and (max-width: 640px) {
header a[href="/play"] { header a[href="/play"] {
display: none; display: none;
} }
@@ -60,10 +74,22 @@ div[class^=HUDButton-module__hiddenContainer] ~ div:not([class^=HUDButton-module
height: 100% !important; height: 100% !important;
} }
.bx-auto-height {
height: auto !important;
}
.bx-no-scroll { .bx-no-scroll {
overflow: hidden !important; overflow: hidden !important;
} }
.bx-hide-scroll-bar {
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
}
.bx-gone { .bx-gone {
display: none !important; display: none !important;
} }
@@ -79,6 +105,19 @@ div[class^=HUDButton-module__hiddenContainer] ~ div:not([class^=HUDButton-module
visibility: hidden !important; visibility: hidden !important;
} }
.bx-invisible {
opacity: 0;
}
.bx-unclickable {
pointer-events: none;
}
.bx-pixel {
width: 1px !important;
height: 1px !important;
}
.bx-no-margin { .bx-no-margin {
margin: 0 !important; margin: 0 !important;
} }
@@ -88,7 +127,54 @@ div[class^=HUDButton-module__hiddenContainer] ~ div:not([class^=HUDButton-module
} }
.bx-prompt { .bx-prompt {
font-family: var(--bx-promptfont-font); font-family: var(--bx-promptfont-font) !important;
}
.bx-monospaced {
font-family: var(--bx-monospaced-font) !important;
}
.bx-line-through {
text-decoration: line-through !important;
}
.bx-normal-case {
text-transform: none !important;
}
.bx-normal-link {
text-transform: none !important;
text-align: left !important;
font-weight: 400 !important;
font-family: var(--bx-normal-font) !important;
}
.bx-frosted {
backdrop-filter: blur(4px) brightness(1.5);
}
select[multiple], select[multiple]:focus {
overflow: auto;
border: none;
option {
padding: 4px 6px;
&:checked {
color = #1a7bc0;
background: color linear-gradient(0deg, color 0%, color 100%);
&::before {
content: '';
font-size: 12px;
display: inline-block;
margin-right: 6px;
height: 100%;
line-height: 100%;
vertical-align: middle;
}
}
}
} }
/* Hide UI elements */ /* Hide UI elements */
@@ -96,14 +182,110 @@ div[class^=HUDButton-module__hiddenContainer] ~ div:not([class^=HUDButton-module
display: none; display: none;
} }
div[class*=NotFocusedDialog] { #game-stream {
position: absolute !important; div[class^=NotFocusedDialog] {
top: -9999px !important; position: absolute !important;
left: -9999px !important; top: -9999px !important;
width: 0px !important; left: -9999px !important;
height: 0px !important; width: 0px !important;
height: 0px !important;
}
video:not([src]) {
visibility: hidden;
}
} }
#game-stream video:not([src]) { .bx-game-tile-wait-time {
visibility: hidden; position: absolute;
top: 0;
left: 0;
z-index: 1;
background: rgba(0, 0, 0, 0.5);
display: flex;
border-radius: 4px 0 4px 0;
align-items: center;
padding: 4px 8px;
svg {
width: 14px;
height: 16px;
margin-right: 2px;
}
span {
display: inline-block;
height: 16px;
line-height: 16px;
font-size: 12px;
font-weight: bold;
margin-left: 2px;
}
&[data-duration=short] {
background-color: rgba(0, 133, 133, 0.75);
}
&[data-duration=medium] {
background-color: rgba(213, 133, 0, 0.75);
}
&[data-duration=long] {
background-color: rgba(150, 0, 0, 0.75);
}
}
.bx-fullscreen-text {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
background: #000000cc;
z-index: var(--bx-fullscreen-text-z-index);
line-height: 100vh;
color: #fff;
text-align: center;
font-weight: 400;
font-family: var(--bx-normal-font);
font-size: 1.3rem;
user-select: none;
-webkit-user-select: none;
}
/* Device Code page */
#root {
section[class*=DeviceCodePage-module__page] {
margin-left: 20px !important;
margin-right: 20px !important;
margin-top: 20px !important;
max-width: 800px !important;
}
div[class*=DeviceCodePage-module__back] {
display: none;
}
}
.bx-blink-me {
animation: bx-blinker 1s linear infinite;
}
@keyframes bx-blinker {
100% {
opacity: 0;
}
}
.bx-horizontal-shaking {
animation: bx-horizontal-shaking .4s ease-in-out 2;
}
@keyframes bx-horizontal-shaking {
0% { transform: translateX(0) }
25% { transform: translateX(5px) }
50% { transform: translateX(-5px) }
75% { transform: translateX(5px) }
100% { transform: translateX(0) }
} }

View File

@@ -0,0 +1,589 @@
.bx-settings-dialog {
display: flex;
position: fixed;
top: 0;
right: 0;
bottom: 0;
opacity: 0.98;
user-select: none;
-webkit-user-select: none;
.bx-settings-reload-note {
font-size: 0.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;
> div:last-of-type {
display: flex;
flex-direction: column;
align-items: end;
button {
flex-shrink: 0;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
margin-top: 8px;
height: unset;
padding: 8px 10px;
svg {
size = 16px;
width: size;
height: size;
}
}
}
}
.bx-settings-tabs {
display: flex;
flex-direction: column;
border-radius: 0 0 0 8px;
box-shadow: 0 0 6px #000;
overflow: overlay;
flex: 1;
svg {
size = 24px;
width: size;
height: size;
padding: 10px;
flex-shrink: 0;
box-sizing: content-box;
background: #131313;
cursor: pointer;
border-left: 4px solid #1e1e1e;
&.bx-active {
background: #222;
border-color: #008746;
}
&:not(.bx-active):hover {
background: #2f2f2f;
border-color: #484848;
}
&:focus {
border-color: #fff;
}
&[data-group=global] {
&[data-need-refresh=true] {
background: var(--bx-danger-button-color) !important;
&:hover {
background: var(--bx-danger-button-hover-color) !important;
}
}
}
}
}
.bx-settings-tab-contents {
tabsWidth = 48px;
flex-direction: column;
margin-left: tabsWidth;
width: 450px;
background: #1a1b1e;
color: #fff;
font-weight: 400;
font-size: 16px;
font-family: var(--bx-title-font);
text-align: center;
box-shadow: 0px 0px 6px #000;
overflow: overlay;
z-index: 1;
.bx-top-buttons {
display: flex;
flex-direction: column;
gap: 8px;
margin-bottom: 8px;
.bx-button {
display: block;
}
}
h2 {
margin: 16px 0 8px 0;
display: flex;
align-items: center;
&:first-of-type {
margin-top: 0;
}
span {
display: inline-block;
font-size: 20px;
font-weight: bold;
text-align: left;
flex: 1;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
min-height: var(--bx-button-height);
align-content: center;
}
}
}
@media (max-width: 500px) {
.bx-settings-tab-contents {
width: calc(100vw - 48px);
}
}
.bx-settings-row {
display: flex;
gap: 10px;
padding: 16px 10px;
background: #2a2a2a;
border-bottom: 1px solid #343434;
&:hover, &:focus-within {
background-color: #242424;
}
&:not(:has(> input[type=checkbox])) {
flex-wrap: wrap;
}
/*
&:has(input:focus), &:has(select:focus), &:has(button:focus) {
border-left-color: white;
}
*/
> span.bx-settings-label {
font-size: 14px;
display: block;
text-align: left;
align-self: center;
margin-bottom: 0 !important;
flex: 1;
svg {
width: 20px;
height: 20px;
margin-inline-end: 8px;
}
+ * {
margin: 0 0 0 auto;
}
}
&[data-multi-lines="true"] {
flex-direction: column;
> span.bx-settings-label {
align-self: start;
+ * {
margin: unset;
}
}
}
&.bx-settings-important-row {
background: #733b00;
}
}
.bx-settings-dialog-note {
display: block;
color: #afafb0;
font-size: 12px;
font-weight: lighter;
font-style: italic;
&:not(:has(a)) {
margin-top: 4px;
}
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;
margin-bottom: 10px;
}
.bx-debug-info {
button {
margin-top: 10px;
}
pre {
margin-top: 10px;
cursor: copy;
color: white;
padding: 8px;
border: 1px solid #2d2d2d;
background: #212121;
white-space: break-spaces;
text-align: left;
&: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-settings-tab-content {
padding: 10px;
border-radius-size = 6px;
> div {
// Label at the beginning
*:not(.bx-settings-row):has(+ .bx-settings-row) + .bx-settings-row:has(+ .bx-settings-row) {
border-top-left-radius: border-radius-size;
border-top-right-radius: border-radius-size;
}
// Label at the end
.bx-settings-row:not(:has(+ .bx-settings-row)) {
border: none;
border-bottom-left-radius: border-radius-size;
border-bottom-right-radius: border-radius-size;
}
// Single label
*:not(.bx-settings-row):has(+ .bx-settings-row) + .bx-settings-row:not(:has(+ .bx-settings-row)) {
border: none;
border-radius: border-radius-size;
}
}
&:not([data-game-id="-1"]) {
.bx-settings-row[data-override=true], .bx-settings-row:has(*[data-override=true]) {
border-left: 4px solid orange !important;
border-top-left-radius: 0 !important;
border-bottom-left-radius: 0 !important;
padding-left: 6px !important;
}
}
}
.bx-suggest-toggler {
text-align: left;
display: flex;
border-radius: 4px;
overflow: hidden;
background: #003861;
height: 45px;
align-items: center;
label {
flex: 1;
align-content: center;
padding: 0 10px;
background: #004f87;
height: 100%;
}
span {
display: inline-block;
align-self: center;
padding: 10px;
width: 45px;
text-align: center;
}
&:hover, &:focus {
cursor: pointer;
background: #005da1;
label {
background: #006fbe;
}
}
&[bx-open] {
span {
transform: rotate(90deg);
}
&+ .bx-suggest-box {
display: block;
}
}
}
.bx-suggest-box {
display: none;
}
.bx-suggest-wrapper {
display: flex;
flex-direction: column;
gap: 10px;
margin: 10px;
}
.bx-suggest-note {
font-size: 11px;
color: #8c8c8c;
font-style: italic;
font-weight: 100;
}
.bx-suggest-link {
font-size: 14px;
display: inline-block;
margin-top: 4px;
padding: 4px;
}
.bx-suggest-row {
display: flex;
flex-direction: row;
gap: 10px;
label {
flex: 1;
overflow: overlay;
border-radius: 4px;
.bx-suggest-label {
background: #323232;
padding: 4px 10px;
font-size: 12px;
text-align: left;
}
.bx-suggest-value {
padding: 6px;
font-size: 14px;
&.bx-suggest-change {
background-color: var(--bx-warning-color);
}
}
}
&.bx-suggest-ok {
input {
visibility: hidden;
}
.bx-suggest-label {
background-color: #008114;
}
.bx-suggest-value {
background-color: #13a72a;
}
}
&.bx-suggest-change {
.bx-suggest-label {
background-color: #a65e08;
}
.bx-suggest-value {
background-color: #d57f18;
}
&:hover {
label {
cursor: pointer;
}
.bx-suggest-label {
background-color: #995707;
}
.bx-suggest-value {
background-color: #bd7115;
}
}
// Unchecked setting
input:not(:checked) + label {
opacity: 0.5;
.bx-suggest-label {
background-color: #2a2a2a;
}
.bx-suggest-value {
background-color: #393939;
}
}
&:hover {
input:not(:checked) + label {
opacity: 1;
.bx-suggest-label {
background-color: #202020;
}
.bx-suggest-value {
background-color: #303030;
}
}
}
}
}
.bx-sub-content-box {
background: #161616;
padding: 10px;
box-shadow: 0px 0px 12px #0f0f0f inset;
border-radius: 10px;
.bx-settings-row & {
background: #202020;
padding: 12px;
box-shadow: 0 0 4px #000000 inset;
border-radius: 6px;
}
}
.bx-controller-extra-settings {
&[data-has-gamepad=true] {
> :first-child {
display: none;
}
> :last-child {
display: block;
}
}
&[data-has-gamepad=false] {
> :first-child {
display: block;
}
> :last-child {
display: none;
}
}
.bx-controller-extra-wrapper {
flex: 1;
min-width: 1px;
}
.bx-sub-content-box {
flex: 1;
text-align: left;
display: flex;
flex-direction: column;
margin-top: 10px;
> label {
font-size: 14px;
}
}
}
.bx-preset-row {
display: flex;
gap: 8px;
.bx-select {
flex: 1;
}
}
.bx-stream-settings-selection {
margin-bottom: 8px;
position: sticky;
z-index: 1000;
top: 0;
> div {
display: flex;
gap: 8px;
background: #222222;
padding: 10px;
border-bottom: 4px solid #353638;
box-shadow: 0 0 6px #000;
position: relative;
z-index: 1;
.bx-select {
flex: 1;
label {
font-weight: bold;
font-size: 1.1rem;
line-height: initial;
span {
line-height: initial;
}
}
.bx-select-indicators {
display: none;
}
}
}
p {
font-family: var(--bx-promptfont-font), var(--bx-normal-font);
margin: 0;
font-size: 13px;
background: #505050f2;
height: 25px;
line-height: 23px;
position: absolute;
bottom: -25px;
left: 0;
right: 0;
text-shadow: 0 1px #000;
}
}

View File

@@ -1,189 +0,0 @@
.bx-stream-settings-dialog {
display: flex;
position: fixed;
z-index: var(--bx-stream-settings-z-index);
opacity: 0.98;
user-select: none;
-webkit-user-select: none;
}
.bx-stream-settings-tabs {
position: fixed;
top: 0;
right: 420px;
display: flex;
flex-direction: column;
border-radius: 0 0 0 8px;
box-shadow: 0px 0px 6px #000;
overflow: clip;
svg {
width: 32px;
height: 32px;
padding: 10px;
box-sizing: content-box;
background: #131313;
cursor: pointer;
border-left: 4px solid #1e1e1e;
&.bx-active {
background: #222;
border-color: #008746;
}
&:not(.bx-active):hover {
background: #2f2f2f;
border-color: #484848;
}
}
}
.bx-stream-settings-tab-contents {
flex-direction: column;
position: fixed;
right: 0;
top: 0;
bottom: 0;
padding: 14px 14px 0;
width: 420px;
background: #1a1b1e;
color: #fff;
font-weight: 400;
font-size: 16px;
font-family: var(--bx-title-font);
text-align: center;
box-shadow: 0px 0px 6px #000;
overflow: overlay;
> div[data-group=mkb] {
display: flex;
flex-direction: column;
height: 100%;
overflow: hidden;
}
*:focus {
outline: none !important;
}
h2 {
margin-bottom: 8px;
display: flex;
align-item: center;
span {
display: inline-block;
font-size: 24px;
font-weight: bold;
text-transform: uppercase;
text-align: left;
flex: 1;
height: var(--bx-button-height);
line-height: calc(var(--bx-button-height) + 4px);
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
}
}
.bx-stream-settings-row {
display: flex;
border-bottom: 1px solid #40404080;
margin-bottom: 16px;
padding-bottom: 16px;
label {
font-size: 16px;
display: block;
text-align: left;
flex: 1;
align-self: center;
margin-bottom: 0 !important;
}
input {
accent-color: var(--bx-primary-button-color);
}
select:disabled {
-webkit-appearance: none;
background: transparent;
text-align-last: right;
border: none;
}
}
.bx-stream-settings-dialog-note {
display: block;
font-size: 12px;
font-weight: lighter;
font-style: italic;
}
.bx-stream-settings-tab-contents {
div[data-group="shortcuts"] {
> div {
&[data-has-gamepad=true] {
> div:first-of-type {
display: none;
}
> div:last-of-type {
display: block;
}
}
&[data-has-gamepad=false] {
> div:first-of-type {
display: block;
}
> div:last-of-type {
display: none;
}
}
}
.bx-shortcut-profile {
width: 100%;
height: 36px;
display: block;
margin-bottom: 10px;
}
.bx-shortcut-note {
font-size: 14px;
}
.bx-shortcut-row {
display: flex;
margin-bottom: 10px;
label.bx-prompt {
flex: 1;
font-size: 26px;
margin-bottom: 0;
}
.bx-shortcut-actions {
flex: 2;
position: relative;
select {
position: absolute;
width: 100%;
height: 100%;
display: block;
&:last-of-type {
opacity: 0;
z-index: calc(var(--bx-stream-settings-z-index) + 1);
}
}
}
}
}
}

78
src/assets/css/stream-stats.styl Normal file → Executable file
View File

@@ -1,6 +1,5 @@
/* STATS BADGE */ /* STATS BADGE */
.bx-badges { .bx-badges {
position: absolute;
margin-left: 0px; margin-left: 0px;
user-select: none; user-select: none;
-webkit-user-select: none; -webkit-user-select: none;
@@ -21,26 +20,60 @@
.bx-badge-name { .bx-badge-name {
background-color: #2d3036; background-color: #2d3036;
display: inline-block;
padding: 2px 8px;
border-radius: 4px 0 0 4px; border-radius: 4px 0 0 4px;
text-transform: uppercase;
svg {
width: 16px;
height: 16px;
}
} }
.bx-badge-value { .bx-badge-value {
background-color: grey; background-color: grey;
display: inline-block;
padding: 2px 8px;
border-radius: 0 4px 4px 0; border-radius: 0 4px 4px 0;
} }
.bx-badge-name, .bx-badge-value {
display: inline-block;
padding: 0 8px;
/* height: 30px; */
line-height: 30px;
vertical-align: bottom;
}
.bx-badge-battery[data-charging=true] span:first-of-type::after { .bx-badge-battery[data-charging=true] span:first-of-type::after {
content: ' '; 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) {
position: unset;
top: unset;
left: unset;
margin: 8px 0;
}
@media (min-width: 480px) and (min-height: calc(481px)) {
}
}
/* STATS BAR */ /* STATS BAR */
.bx-stats-bar { .bx-stats-bar {
display: block; display: flex;
flex-direction: row;
gap: 8px;
user-select: none; user-select: none;
-webkit-user-select: none; -webkit-user-select: none;
position: fixed; position: fixed;
@@ -53,22 +86,34 @@
z-index: var(--bx-stats-bar-z-index); z-index: var(--bx-stats-bar-z-index);
text-wrap: nowrap; text-wrap: nowrap;
&[data-stats*="[time]"] > .bx-stat-time,
&[data-stats*="[play]"] > .bx-stat-play,
&[data-stats*="[batt]"] > .bx-stat-batt,
&[data-stats*="[fps]"] > .bx-stat-fps, &[data-stats*="[fps]"] > .bx-stat-fps,
&[data-stats*="[ping]"] > .bx-stat-ping, &[data-stats*="[ping]"] > .bx-stat-ping,
&[data-stats*="[jit]"] > .bx-stat-jit,
&[data-stats*="[btr]"] > .bx-stat-btr, &[data-stats*="[btr]"] > .bx-stat-btr,
&[data-stats*="[dt]"] > .bx-stat-dt, &[data-stats*="[dt]"] > .bx-stat-dt,
&[data-stats*="[pl]"] > .bx-stat-pl, &[data-stats*="[pl]"] > .bx-stat-pl,
&[data-stats*="[fl]"] > .bx-stat-fl { &[data-stats*="[fl]"] > .bx-stat-fl,
display: inline-block; &[data-stats*="[dl]"] > .bx-stat-dl,
&[data-stats*="[ul]"] > .bx-stat-ul {
display: inline-flex;
align-items: baseline;
} }
&[data-stats$="[time]"] > .bx-stat-time,
&[data-stats$="[play]"] > .bx-stat-play,
&[data-stats$="[batt]"] > .bx-stat-batt,
&[data-stats$="[fps]"] > .bx-stat-fps, &[data-stats$="[fps]"] > .bx-stat-fps,
&[data-stats$="[ping]"] > .bx-stat-ping, &[data-stats$="[ping]"] > .bx-stat-ping,
&[data-stats$="[jit]"] > .bx-stat-jit,
&[data-stats$="[btr]"] > .bx-stat-btr, &[data-stats$="[btr]"] > .bx-stat-btr,
&[data-stats$="[dt]"] > .bx-stat-dt, &[data-stats$="[dt]"] > .bx-stat-dt,
&[data-stats$="[pl]"] > .bx-stat-pl, &[data-stats$="[pl]"] > .bx-stat-pl,
&[data-stats$="[fl]"] > .bx-stat-fl { &[data-stats$="[fl]"] > .bx-stat-fl,
margin-right: 0; &[data-stats$="[dl]"] > .bx-stat-dl,
&[data-stats$="[ul]"] > .bx-stat-ul {
border-right: none; border-right: none;
} }
@@ -99,14 +144,13 @@
border-radius: 0 0 4px 4px; border-radius: 0 0 4px 4px;
} }
&[data-transparent=true] { &[data-shadow=true] {
background: none; background: none;
filter: drop-shadow(1px 0 0 #000000f0) drop-shadow(-1px 0 0 #000000f0) drop-shadow(0 1px 0 #000000f0) drop-shadow(0 -1px 0 #000000f0); filter: drop-shadow(1px 0 0 #000000f0) drop-shadow(-1px 0 0 #000000f0) drop-shadow(0 1px 0 #000000f0) drop-shadow(0 -1px 0 #000000f0);
} }
> div { > div {
display: none; display: none;
margin-right: 8px;
border-right: 1px solid #fff; border-right: 1px solid #fff;
padding-right: 8px; padding-right: 8px;
} }
@@ -114,17 +158,17 @@
label { label {
margin: 0 8px 0 0; margin: 0 8px 0 0;
font-family: var(--bx-title-font); font-family: var(--bx-title-font);
font-size: inherit; font-size: 70%;
font-weight: bold; font-weight: bold;
vertical-align: middle; vertical-align: middle;
cursor: help; cursor: help;
} }
span { span {
min-width: 60px;
display: inline-block; display: inline-block;
text-align: right; text-align: right;
vertical-align: middle; vertical-align: middle;
white-space: pre;
&[data-grade=good] { &[data-grade=good] {
color: #6bffff; color: #6bffff;
@@ -137,9 +181,5 @@
&[data-grade=bad] { &[data-grade=bad] {
color: #ff5f5f; color: #ff5f5f;
} }
&:first-of-type {
min-width: 22px;
}
} }
} }

60
src/assets/css/stream.styl Normal file → Executable file
View File

@@ -1,4 +1,4 @@
div[class*=StreamMenu-module__menuContainer] > div[class*=Menu-module] { #game-stream div[class^=StreamMenu-module__menuContainer] > div[class^=Menu-module] {
overflow: visible; overflow: visible;
} }
@@ -20,6 +20,18 @@ body[data-media-type=tv] .bx-stream-refresh-button {
top: calc(var(--gds-focus-borderSize) + 80px) !important; 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;
}
@keyframes bx-anim-taking-screenshot { @keyframes bx-anim-taking-screenshot {
0% { 0% {
border: 0px solid #ffffff80; border: 0px solid #ffffff80;
@@ -35,7 +47,41 @@ body[data-media-type=tv] .bx-stream-refresh-button {
} }
div[data-testid=media-container] { div[data-testid=media-container] {
display: flex; &[data-position=center] {
display: flex;
}
&[data-position=top] {
video, canvas {
top: 0;
}
}
&[data-position=bottom] {
video, canvas {
bottom: 0;
}
}
}
#game-stream {
video {
margin: auto;
align-self: center;
background: #000;
position: absolute;
left: 0;
right: 0;
}
canvas {
align-self: center;
margin: auto;
position: absolute;
left: 0;
right: 0;
}
&.bx-taking-screenshot:before { &.bx-taking-screenshot:before {
animation: bx-anim-taking-screenshot 0.5s ease; animation: bx-anim-taking-screenshot 0.5s ease;
@@ -47,9 +93,9 @@ div[data-testid=media-container] {
} }
} }
#gamepass-dialog-root div[class^=Guide-module__guide] {
#game-stream video { .bx-button {
margin: auto; overflow: visible;
align-self: center; margin-bottom: 12px;
background: #000; }
} }

10
src/assets/css/styles.styl Normal file → Executable file
View File

@@ -2,15 +2,19 @@
@import 'button.styl'; @import 'button.styl';
@import 'header.styl'; @import 'header.styl';
@import 'global-settings.styl'; @import 'key-binding-dialog.styl';
@import 'dialog.styl'; @import 'navigation-dialog.styl';
@import 'settings-dialog.styl';
@import 'toast.styl'; @import 'toast.styl';
@import 'loading-screen.styl'; @import 'loading-screen.styl';
@import 'remote-play.styl'; @import 'remote-play.styl';
@import 'web-components.styl';
@import 'guide-menu.styl';
@import 'stream.styl'; @import 'stream.styl';
@import 'number-stepper.styl'; @import 'number-stepper.styl';
@import 'game-bar.styl'; @import 'game-bar.styl';
@import 'stream-stats.styl'; @import 'stream-stats.styl';
@import 'stream-settings.styl';
@import 'mkb.styl'; @import 'mkb.styl';
@import 'controller.styl';
@import 'misc.styl';

11
src/assets/css/toast.styl Normal file → Executable file
View File

@@ -5,8 +5,8 @@
left: 50%; left: 50%;
top: 24px; top: 24px;
transform: translate(-50%, 0); transform: translate(-50%, 0);
background: #000000; background: #212121;
border-radius: 16px; border-radius: 10px;
color: white; color: white;
z-index: var(--bx-toast-z-index); z-index: var(--bx-toast-z-index);
font-family: var(--bx-normal-font); font-family: var(--bx-normal-font);
@@ -16,9 +16,10 @@
opacity: 0; opacity: 0;
overflow: clip; overflow: clip;
transition: opacity 0.2s ease-in; transition: opacity 0.2s ease-in;
box-shadow: 0 0 6px #000;
&.bx-show { &.bx-show {
opacity: 0.85; opacity: 0.95;
} }
&.bx-hide { &.bx-hide {
@@ -39,8 +40,8 @@
font-size: 14px; font-size: 14px;
text-transform: uppercase; text-transform: uppercase;
display: inline-block; display: inline-block;
background: #515863; background: #fff;
padding: 12px 16px; padding: 12px 16px;
color: #fff; color: #212121;
white-space: pre; white-space: pre;
} }

View File

@@ -0,0 +1,216 @@
select.bx-select {
min-height: 30px;
}
div.bx-select {
display: flex;
align-items: stretch;
flex: 0 1 auto;
gap: 8px;
select {
&:disabled {
& ~ button {
display: none;
}
& ~ div {
background: #131416;
color: white;
pointer-events: none;
.bx-select-indicators {
visibility: hidden;
}
}
}
}
> div, button.bx-select-value {
min-width: 120px;
text-align: left;
line-height: 24px;
vertical-align: middle;
background: #fff;
color: #000;
border-radius: 4px;
padding: 2px 8px;
display: flex;
flex: 1;
flex-direction: column;
}
> div {
min-height: 24px;
input {
display: inline-block;
margin-right: 8px;
}
label {
margin-bottom: 0;
font-size: 14px;
width: 100%;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
min-height: 15px;
span {
display: block;
font-size: 10px;
font-weight: bold;
text-align: left;
line-height: 20px;
white-space: pre;
min-height: 15px;
align-content: center;
}
}
}
button.bx-select-value {
border: none;
cursor: pointer;
min-height: 30px;
font-size: 0.9rem;
align-items: center;
> div {
display: flex;
width: 100%;
}
span {
flex: 1;
text-align: left;
display: inline-block;
}
input {
margin: 0 4px;
accent-color: var(--bx-primary-button-color);
pointer-events: none;
}
&:hover,
&:focus {
input {
accent-color: var(--bx-danger-button-color);
}
&::after {
border-color: #4d4d4d !important;
}
}
}
button.bx-button {
border: none;
width: 24px;
height: auto;
padding: 0;
color: #fff;
border-radius: 4px;
font-weight: bold;
font-size: 12px;
font-family: var(--bx-monospaced-font);
flex-shrink: 0;
span {
line-height: unset;
}
}
&[data-controller-friendly=true] {
> div {
box-sizing: content-box;
}
select {
// Render offscreen instead of "display: none" so we could get its size
position: absolute !important;
top: -9999px !important;
left: -9999px !important;
visibility: hidden !important;
}
}
&[data-controller-friendly=false] {
position: relative;
> div {
box-sizing: border-box;
label {
margin-right: 24px;
}
}
select {
&:disabled {
display: none;
}
&:not(:disabled) {
cursor: pointer;
position: absolute;
top: 0;
right: 0;
bottom: 0;
display: block;
opacity: 0;
z-index: calc(var(--bx-settings-z-index) + 1);
&:hover {
+ div {
background: #f0f0f0;
}
}
+ div {
label {
&::after {
content: '';
font-size: 14px;
position: absolute;
right: 8px;
pointer-events: none;
}
}
}
}
}
}
}
.bx-select-indicators {
display: flex;
height: 4px;
gap: 2px;
margin-bottom: 2px;
span {
content: ' ';
display: inline-block;
flex: 1;
background: #cfcfcf;
border-radius: 4px;
min-width: 1px;
&[data-highlighted] {
background: #9c9c9c;
min-width: 6px;
}
&[data-selected] {
background: #aacfe7;
}
&[data-highlighted][data-selected] {
background: #5fa3d0;
}
}
}

0
src/assets/header_meta.txt Normal file → Executable file
View File

View File

@@ -0,0 +1,13 @@
// ==UserScript==
// @name Better xCloud (Lite)
// @namespace https://github.com/redphx
// @version [[VERSION]]
// @description Improve Xbox Cloud Gaming (xCloud) experience
// @author redphx
// @license MIT
// @match https://www.xbox.com/*/play*
// @match https://www.xbox.com/*/auth/msa?*loggedIn*
// @run-at document-end
// @grant none
// ==/UserScript==
"use strict";

2
src/assets/header_script.txt Normal file → Executable file
View File

@@ -12,4 +12,4 @@
// @updateURL https://raw.githubusercontent.com/redphx/better-xcloud/typescript/dist/better-xcloud.meta.js // @updateURL https://raw.githubusercontent.com/redphx/better-xcloud/typescript/dist/better-xcloud.meta.js
// @downloadURL https://github.com/redphx/better-xcloud/releases/latest/download/better-xcloud.user.js // @downloadURL https://github.com/redphx/better-xcloud/releases/latest/download/better-xcloud.user.js
// ==/UserScript== // ==/UserScript==
'use strict'; "use strict";

View File

@@ -0,0 +1,3 @@
<svg xmlns='http://www.w3.org/2000/svg' fill='#fff' fill-rule='evenodd' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' stroke-miterlimit='2' viewBox='0 0 32 32'>
<path d='M24.774 6.71H3.097C1.398 6.71 0 8.108 0 9.806v12.387c0 1.699 1.398 3.097 3.097 3.097h21.677c1.699 0 3.097-1.398 3.097-3.097V9.806c0-1.699-1.398-3.097-3.097-3.097zm1.032 15.484a1.04 1.04 0 0 1-1.032 1.032H3.097a1.04 1.04 0 0 1-1.032-1.032V9.806a1.04 1.04 0 0 1 1.032-1.032h21.677a1.04 1.04 0 0 1 1.032 1.032v12.387zm-2.065-10.323v8.258a1.04 1.04 0 0 1-1.032 1.032H5.161a1.04 1.04 0 0 1-1.032-1.032v-8.258a1.04 1.04 0 0 1 1.032-1.032H22.71a1.04 1.04 0 0 1 1.032 1.032zm8.258 0v8.258a1.04 1.04 0 0 1-1.032 1.032 1.04 1.04 0 0 1-1.032-1.032v-8.258a1.04 1.04 0 0 1 1.032-1.032A1.04 1.04 0 0 1 32 11.871z' fill-rule='nonzero'/>
</svg>

After

Width:  |  Height:  |  Size: 821 B

View File

@@ -0,0 +1,8 @@
<svg xmlns='http://www.w3.org/2000/svg' fill='none' stroke='none' fill-rule='evenodd' viewBox='0 0 32 32'>
<clipPath id='svg-bx-logo'>
<path d='M0 0h32v32H0z'/>
</clipPath>
<g clip-path='url(#svg-bx-logo)'>
<path d='M19.959 18.286l3.959 2.285-3.959 2.286V32L16 29.714v-9.143l3.959-2.285zM16 16V6.857l3.959-2.286 3.959 2.286-3.959 2.286v9.143L16 16zm-3.959-2.286L16 16l-3.959 2.286v9.143l-3.959-2.286V16l3.959-2.286zM8.082 2.286L12.041 0 16 2.286l-3.959 2.285v9.143l-3.959-2.285V2.286zm8.846 19.535c-.171-.098-.309-.018-.309.179s.138.437.309.536.309.018.309-.179-.138-.437-.309-.536zm0-13.714c-.171-.098-.309-.018-.309.179s.138.437.309.535.309.019.309-.178-.138-.437-.309-.536zM9.01 17.25c-.171-.099-.309-.019-.309.179s.138.437.309.535.309.019.309-.178-.138-.437-.309-.536zm0-13.714c-.171-.099-.309-.019-.309.178s.138.437.309.536.309.019.309-.179-.138-.437-.309-.535z' fill='#fff'/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 926 B

6
src/assets/svg/camera.svg Normal file → Executable file
View File

@@ -1,6 +1,6 @@
<svg xmlns='http://www.w3.org/2000/svg' fill='none' stroke='#fff' fill-rule='evenodd' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' viewBox='0 0 32 32'> <svg xmlns='http://www.w3.org/2000/svg' fill='none' stroke='#fff' fill-rule='evenodd' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' viewBox='0 0 32 32'>
<g transform="matrix(.150985 0 0 .150985 -3.32603 -2.72209)" fill="none" stroke="#fff" stroke-width="16"> <g transform='matrix(.150985 0 0 .150985 -3.32603 -2.72209)' fill='none' stroke='#fff' stroke-width='16'>
<path d="M208 208H48c-8.777 0-16-7.223-16-16V80c0-8.777 7.223-16 16-16h32l16-24h64l16 24h32c8.777 0 16 7.223 16 16v112c0 8.777-7.223 16-16 16z"/> <path d='M208 208H48c-8.777 0-16-7.223-16-16V80c0-8.777 7.223-16 16-16h32l16-24h64l16 24h32c8.777 0 16 7.223 16 16v112c0 8.777-7.223 16-16 16z'/>
<circle cx="128" cy="132" r="36"/> <circle cx='128' cy='132' r='36'/>
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 494 B

After

Width:  |  Height:  |  Size: 494 B

4
src/assets/svg/caret-left.svg Normal file → Executable file
View File

@@ -1,3 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="100%" stroke="#fff" fill="#fff" height="100%" viewBox="0 0 32 32" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2"> <svg xmlns='http://www.w3.org/2000/svg' width='100%' stroke='#fff' fill='#fff' height='100%' viewBox='0 0 32 32' fill-rule='evenodd' stroke-linejoin='round' stroke-miterlimit='2'>
<path d="M6.755 1.924l-6 13.649c-.119.27-.119.578 0 .849l6 13.649c.234.533.857.775 1.389.541s.775-.857.541-1.389L2.871 15.997 8.685 2.773c.234-.533-.008-1.155-.541-1.389s-1.155.008-1.389.541z"/> <path d='M6.755 1.924l-6 13.649c-.119.27-.119.578 0 .849l6 13.649c.234.533.857.775 1.389.541s.775-.857.541-1.389L2.871 15.997 8.685 2.773c.234-.533-.008-1.155-.541-1.389s-1.155.008-1.389.541z'/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 386 B

After

Width:  |  Height:  |  Size: 386 B

4
src/assets/svg/caret-right.svg Normal file → Executable file
View File

@@ -1,3 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="100%" stroke="#fff" fill="#fff" height="100%" viewBox="0 0 32 32" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2"> <svg xmlns='http://www.w3.org/2000/svg' width='100%' stroke='#fff' fill='#fff' height='100%' viewBox='0 0 32 32' fill-rule='evenodd' stroke-linejoin='round' stroke-miterlimit='2'>
<path d="M2.685 1.924l6 13.649c.119.27.119.578 0 .849l-6 13.649c-.234.533-.857.775-1.389.541s-.775-.857-.541-1.389l5.813-13.225L.755 2.773c-.234-.533.008-1.155.541-1.389s1.155.008 1.389.541z"/> <path d='M2.685 1.924l6 13.649c.119.27.119.578 0 .849l-6 13.649c-.234.533-.857.775-1.389.541s-.775-.857-.541-1.389l5.813-13.225L.755 2.773c-.234-.533.008-1.155.541-1.389s1.155.008 1.389.541z'/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 385 B

After

Width:  |  Height:  |  Size: 385 B

6
src/assets/svg/clock.svg Executable file
View File

@@ -0,0 +1,6 @@
<svg xmlns='http://www.w3.org/2000/svg' fill='none' stroke='#fff' fill-rule='evenodd' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' viewBox='0 0 32 32'>
<g transform='matrix(.150026 0 0 .150026 -3.20332 -3.20332)' fill='none' stroke='#fff' stroke-width='16'>
<circle cx='128' cy='128' r='96'/>
<path d='M128 72v56h56'/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 374 B

4
src/assets/svg/close.svg Executable file
View File

@@ -0,0 +1,4 @@
<svg xmlns='http://www.w3.org/2000/svg' fill='none' stroke='#fff' fill-rule='evenodd' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' viewBox='0 0 32 32'>
<path d='M29.928,2.072L2.072,29.928'/>
<path d='M29.928,29.928L2.072,2.072'/>
</svg>

After

Width:  |  Height:  |  Size: 264 B

3
src/assets/svg/cloud.svg Executable file
View File

@@ -0,0 +1,3 @@
<svg xmlns='http://www.w3.org/2000/svg' fill='none' stroke='#fff' fill-rule='evenodd' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' viewBox='0 0 32 32'>
<path d='M9.773 16c0-5.694 4.685-10.379 10.379-10.379S30.53 10.306 30.53 16s-4.685 10.379-10.379 10.379H8.735c-3.982-.005-7.256-3.283-7.256-7.265s3.28-7.265 7.265-7.265c.606 0 1.21.076 1.797.226' fill='none' stroke='#fff' stroke-width='2.076'/>
</svg>

After

Width:  |  Height:  |  Size: 427 B

4
src/assets/svg/command.svg Normal file → Executable file
View File

@@ -1,4 +1,4 @@
<svg xmlns='http://www.w3.org/2000/svg' fill='none' stroke='#fff' fill-rule='evenodd' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' viewBox='0 0 32 32'> <svg xmlns='http://www.w3.org/2000/svg' fill='none' stroke='#fff' fill-rule='evenodd' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' viewBox='0 0 32 32'>
<path d="M25.425 1.5c2.784 0 5.075 2.291 5.075 5.075s-2.291 5.075-5.075 5.075H20.35V6.575c0-2.784 2.291-5.075 5.075-5.075zM11.65 11.65H6.575C3.791 11.65 1.5 9.359 1.5 6.575S3.791 1.5 6.575 1.5s5.075 2.291 5.075 5.075v5.075zm8.7 8.7h5.075c2.784 0 5.075 2.291 5.075 5.075S28.209 30.5 25.425 30.5s-5.075-2.291-5.075-5.075V20.35zM6.575 30.5c-2.784 0-5.075-2.291-5.075-5.075s2.291-5.075 5.075-5.075h5.075v5.075c0 2.784-2.291 5.075-5.075 5.075z"/> <path d='M25.425 1.5c2.784 0 5.075 2.291 5.075 5.075s-2.291 5.075-5.075 5.075H20.35V6.575c0-2.784 2.291-5.075 5.075-5.075zM11.65 11.65H6.575C3.791 11.65 1.5 9.359 1.5 6.575S3.791 1.5 6.575 1.5s5.075 2.291 5.075 5.075v5.075zm8.7 8.7h5.075c2.784 0 5.075 2.291 5.075 5.075S28.209 30.5 25.425 30.5s-5.075-2.291-5.075-5.075V20.35zM6.575 30.5c-2.784 0-5.075-2.291-5.075-5.075s2.291-5.075 5.075-5.075h5.075v5.075c0 2.784-2.291 5.075-5.075 5.075z'/>
<path d="M11.65 11.65h8.7v8.7h-8.7z"/> <path d='M11.65 11.65h8.7v8.7h-8.7z'/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 667 B

After

Width:  |  Height:  |  Size: 667 B

0
src/assets/svg/controller.svg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 646 B

After

Width:  |  Height:  |  Size: 646 B

0
src/assets/svg/copy.svg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 250 B

After

Width:  |  Height:  |  Size: 250 B

View File

@@ -0,0 +1,4 @@
<svg xmlns='http://www.w3.org/2000/svg' fill='none' stroke='#fff' fill-rule='evenodd' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' viewBox='0 0 32 32'>
<path d='M13.253 3.639c0-.758-.615-1.373-1.373-1.373H3.639c-.758 0-1.373.615-1.373 1.373v8.241c0 .758.615 1.373 1.373 1.373h8.241c.758 0 1.373-.615 1.373-1.373V3.639zm0 16.481c0-.758-.615-1.373-1.373-1.373H3.639c-.758 0-1.373.615-1.373 1.373v8.241c0 .758.615 1.373 1.373 1.373h8.241c.758 0 1.373-.615 1.373-1.373V20.12zm16.481 0c0-.758-.615-1.373-1.373-1.373H20.12c-.758 0-1.373.615-1.373 1.373v8.241c0 .758.615 1.373 1.373 1.373h8.241c.758 0 1.373-.615 1.373-1.373V20.12zM19.262 7.76h9.957'/>
<path d='M24.24 2.781v9.957'/>
</svg>

After

Width:  |  Height:  |  Size: 711 B

0
src/assets/svg/cursor-text.svg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 370 B

After

Width:  |  Height:  |  Size: 370 B

0
src/assets/svg/display.svg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 382 B

After

Width:  |  Height:  |  Size: 382 B

4
src/assets/svg/download.svg Executable file
View File

@@ -0,0 +1,4 @@
<svg xmlns='http://www.w3.org/2000/svg' fill='none' stroke='#fff' fill-rule='evenodd' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' viewBox='0 0 32 32'>
<path d='M16 19.955V1.5m14.5 18.455v9.227c0 .723-.595 1.318-1.318 1.318H2.818c-.723 0-1.318-.595-1.318-1.318v-9.227'/>
<path d='M22.591 13.364L16 19.955l-6.591-6.591'/>
</svg>

After

Width:  |  Height:  |  Size: 355 B

8
src/assets/svg/eye-slash.svg Executable file
View File

@@ -0,0 +1,8 @@
<svg xmlns='http://www.w3.org/2000/svg' fill='#fff' stroke='none ' fill-rule='evenodd' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' viewBox='0 0 32 32'>
<clipPath id='A'>
<path d='M0 0h32v32H0z'/>
</clipPath>
<g clip-path='url(#A)'>
<path d='M6.123 3.549a1.07 1.07 0 0 0-.798-.359c-.585 0-1.067.482-1.067 1.067 0 .27.102.53.286.727l2.565 2.823C2.267 10.779.184 15.36.092 15.568c-.123.276-.123.591 0 .867.047.105 1.176 2.609 3.687 5.12 3.345 3.344 7.57 5.112 12.221 5.112a16.97 16.97 0 0 0 6.943-1.444l2.933 3.228c.202.228.493.359.798.359.585 0 1.067-.482 1.067-1.067a1.07 1.07 0 0 0-.286-.727L6.123 3.549zm6.31 10.112l5.556 6.114c-.612.322-1.294.49-1.986.49a4.29 4.29 0 0 1-4.267-4.266c0-.831.242-1.643.697-2.338zM16 24.533c-4.104 0-7.689-1.492-10.657-4.433A17.73 17.73 0 0 1 2.267 16c.625-1.172 2.621-4.452 6.313-6.584l2.4 2.633c-.878 1.125-1.356 2.512-1.356 3.939 0 3.511 2.89 6.4 6.4 6.4 1.221 0 2.416-.349 3.444-1.005l1.964 2.16a14.92 14.92 0 0 1-5.432.99zm.8-12.724a1.07 1.07 0 0 1-.867-1.048c0-.585.482-1.067 1.067-1.067a1.12 1.12 0 0 1 .2.019c2.784.54 4.896 2.863 5.169 5.686a1.07 1.07 0 0 1-.962 1.161c-.034.002-.067.002-.1 0a1.07 1.07 0 0 1-1.067-.968 4.29 4.29 0 0 0-3.44-3.783zm15.104 4.626c-.056.125-1.407 3.116-4.448 5.84a1.07 1.07 0 0 1-.724.283c-.585 0-1.067-.482-1.067-1.067a1.07 1.07 0 0 1 .368-.806A17.7 17.7 0 0 0 29.74 16a17.73 17.73 0 0 0-3.083-4.103C23.689 8.959 20.104 7.467 16 7.467a15.82 15.82 0 0 0-2.581.209 1.06 1.06 0 0 1-.186.016 1.07 1.07 0 0 1-1.067-1.066 1.07 1.07 0 0 1 .901-1.054A17.89 17.89 0 0 1 16 5.333c4.651 0 8.876 1.768 12.221 5.114 2.511 2.51 3.64 5.016 3.687 5.121.123.276.123.591 0 .867h-.004z' fill-rule='nonzero'/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

8
src/assets/svg/eye.svg Executable file
View File

@@ -0,0 +1,8 @@
<svg xmlns='http://www.w3.org/2000/svg' fill='#fff' stroke='none ' fill-rule='evenodd' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' viewBox='0 0 32 32'>
<clipPath id='A'>
<path d='M0 0h32v32H0z'/>
</clipPath>
<g clip-path='url(#A)'>
<path d='M31.908 15.568c-.047-.105-1.176-2.611-3.687-5.121C24.876 7.101 20.651 5.333 16 5.333S7.124 7.101 3.779 10.447c-2.511 2.51-3.646 5.02-3.687 5.121-.123.276-.123.591 0 .867.047.105 1.176 2.609 3.687 5.12 3.345 3.344 7.57 5.112 12.221 5.112s8.876-1.768 12.221-5.112c2.511-2.511 3.64-5.015 3.687-5.12.123-.276.123-.591 0-.867zM16 24.533c-4.104 0-7.689-1.492-10.657-4.433-1.218-1.211-2.254-2.592-3.076-4.1.822-1.508 1.858-2.889 3.076-4.1C8.311 8.959 11.896 7.467 16 7.467s7.689 1.492 10.657 4.433c1.221 1.211 2.259 2.592 3.083 4.1-.961 1.795-5.149 8.533-13.74 8.533zM16 9.6c-3.511 0-6.4 2.889-6.4 6.4s2.889 6.4 6.4 6.4 6.4-2.889 6.4-6.4A6.44 6.44 0 0 0 16 9.6zm0 10.667A4.29 4.29 0 0 1 11.733 16 4.29 4.29 0 0 1 16 11.733 4.29 4.29 0 0 1 20.267 16 4.29 4.29 0 0 1 16 20.267z' fill-rule='nonzero'/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,6 @@
<svg xmlns='http://www.w3.org/2000/svg' fill='none' stroke='#fff' fill-rule='evenodd' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' viewBox='0 0 32 32'>
<path d='M1.681 16h28.638'/>
<path d='M16 30.319C8.145 30.319 1.681 23.855 1.681 16S8.145 1.681 16 1.681 30.319 8.145 30.319 16'/>
<path d='M16 30.319S10.034 25.546 10.034 16 16 1.681 16 1.681 21.966 6.454 21.966 16m-.238 8.592l-2.864 2.864 2.864 2.863'/>
<path d='M21.728 20.773h5.25a3.36 3.36 0 0 1 3.341 3.341 3.36 3.36 0 0 1-3.341 3.342h-8.114'/>
</svg>

After

Width:  |  Height:  |  Size: 545 B

3
src/assets/svg/home.svg Executable file
View File

@@ -0,0 +1,3 @@
<svg xmlns='http://www.w3.org/2000/svg' fill='none' stroke='#fff' fill-rule='evenodd' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' viewBox='0 0 32 32'>
<path d='M12.217 30.503V20.414h7.567v10.089h10.089V15.37a1.26 1.26 0 0 0-.369-.892L16.892 1.867a1.26 1.26 0 0 0-1.784 0L2.497 14.478a1.26 1.26 0 0 0-.369.892v15.133h10.089z'/>
</svg>

After

Width:  |  Height:  |  Size: 358 B

View File

@@ -0,0 +1,7 @@
<svg xmlns='http://www.w3.org/2000/svg' width='1em' height='1em' viewBox='0 0 32 32' fill-rule='evenodd' stroke-linecap='round' stroke-linejoin='round'>
<g>
<path d='M24.272 11.165h-3.294l-3.14 3.564c-.391.391-.922.611-1.476.611a2.1 2.1 0 0 1-2.087-2.088 2.09 2.09 0 0 1 .031-.362l1.22-6.274a3.89 3.89 0 0 1 3.81-3.206h6.57c1.834 0 3.439 1.573 3.833 3.295l1.205 6.185a2.09 2.09 0 0 1 .031.362 2.1 2.1 0 0 1-2.087 2.088c-.554 0-1.085-.22-1.476-.611l-3.14-3.564' fill='none' stroke='#fff' stroke-width='2'/>
<circle cx='22.625' cy='5.874' r='.879'/><path d='M11.022 24.415H7.728l-3.14 3.564c-.391.391-.922.611-1.476.611a2.1 2.1 0 0 1-2.087-2.088 2.09 2.09 0 0 1 .031-.362l1.22-6.274a3.89 3.89 0 0 1 3.81-3.206h6.57c1.834 0 3.439 1.573 3.833 3.295l1.205 6.185a2.09 2.09 0 0 1 .031.362 2.1 2.1 0 0 1-2.087 2.088c-.554 0-1.085-.22-1.476-.611l-3.14-3.564' fill='none' stroke='#fff' stroke-width='2'/>
<circle cx='9.375' cy='19.124' r='.879'/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 981 B

4
src/assets/svg/microphone-slash.svg Normal file → Executable file
View File

@@ -1,4 +1,4 @@
<svg xmlns='http://www.w3.org/2000/svg' fill='none' stroke='#fff' fill-rule='evenodd' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' viewBox='0 0 32 32'> <svg xmlns='http://www.w3.org/2000/svg' fill='none' stroke='#fff' fill-rule='evenodd' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' viewBox='0 0 32 32'>
<path d="M16 25.125v5.368M5.265 4.728l21.471 23.618m-4.789-5.267c-1.698 1.326-3.793 2.047-5.947 2.047-5.3 0-9.662-4.362-9.662-9.662"/> <path d='M16 25.125v5.368M5.265 4.728l21.471 23.618m-4.789-5.267c-1.698 1.326-3.793 2.047-5.947 2.047-5.3 0-9.662-4.362-9.662-9.662'/>
<path d="M25.662 15.463a9.62 9.62 0 0 1-.978 4.242m-5.64.187c-.895.616-1.957.943-3.043.939-2.945 0-5.368-2.423-5.368-5.368v-4.831m.442-5.896A5.38 5.38 0 0 1 16 1.507c2.945 0 5.368 2.423 5.368 5.368v8.588c0 .188-.01.375-.03.562"/> <path d='M25.662 15.463a9.62 9.62 0 0 1-.978 4.242m-5.64.187c-.895.616-1.957.943-3.043.939-2.945 0-5.368-2.423-5.368-5.368v-4.831m.442-5.896A5.38 5.38 0 0 1 16 1.507c2.945 0 5.368 2.423 5.368 5.368v8.588c0 .188-.01.375-.03.562'/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 551 B

After

Width:  |  Height:  |  Size: 551 B

2
src/assets/svg/microphone.svg Normal file → Executable file
View File

@@ -1,3 +1,3 @@
<svg xmlns='http://www.w3.org/2000/svg' fill='none' stroke='#fff' fill-rule='evenodd' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' viewBox='0 0 32 32'> <svg xmlns='http://www.w3.org/2000/svg' fill='none' stroke='#fff' fill-rule='evenodd' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' viewBox='0 0 32 32'>
<path d="M21.368 6.875A5.37 5.37 0 0 0 16 1.507a5.37 5.37 0 0 0-5.368 5.368v8.588A5.37 5.37 0 0 0 16 20.831a5.37 5.37 0 0 0 5.368-5.368V6.875zM16 25.125v5.368m9.662-15.03c0 5.3-4.362 9.662-9.662 9.662s-9.662-4.362-9.662-9.662"/> <path d='M21.368 6.875A5.37 5.37 0 0 0 16 1.507a5.37 5.37 0 0 0-5.368 5.368v8.588A5.37 5.37 0 0 0 16 20.831a5.37 5.37 0 0 0 5.368-5.368V6.875zM16 25.125v5.368m9.662-15.03c0 5.3-4.362 9.662-9.662 9.662s-9.662-4.362-9.662-9.662'/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 411 B

After

Width:  |  Height:  |  Size: 411 B

0
src/assets/svg/mouse-settings.svg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 981 B

After

Width:  |  Height:  |  Size: 981 B

0
src/assets/svg/mouse.svg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 344 B

After

Width:  |  Height:  |  Size: 344 B

10
src/assets/svg/native-mkb.svg Executable file
View File

@@ -0,0 +1,10 @@
<svg xmlns='http://www.w3.org/2000/svg' fill='none' stroke='#fff' fill-rule='evenodd' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' viewBox='0 0 32 32'>
<g stroke-width='2.1'>
<path d='m15.817 6h-10.604c-2.215 0-4.013 1.798-4.013 4.013v12.213c0 2.215 1.798 4.013 4.013 4.013h11.21'/>
<path d='m5.698 20.617h1.124m-1.124-4.517h7.9m-7.881-4.5h7.9m-2.3 9h2.2'/>
</g>
<g stroke-width='2.13'>
<path d='m30.805 13.1c0-3.919-3.181-7.1-7.1-7.1s-7.1 3.181-7.1 7.1v6.4c0 3.919 3.182 7.1 7.1 7.1s7.1-3.181 7.1-7.1z'/>
<path d='m23.705 14.715v-4.753'/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 619 B

0
src/assets/svg/new.svg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 411 B

After

Width:  |  Height:  |  Size: 411 B

View File

@@ -0,0 +1,4 @@
<svg xmlns='http://www.w3.org/2000/svg' fill='none' stroke='#fff' fill-rule='evenodd' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' viewBox='0 0 32 32'>
<path d='M10.417 30.271H2.97a1.25 1.25 0 0 1-1.241-1.241v-6.933c.001-.329.131-.644.363-.877L21.223 2.09c.481-.481 1.273-.481 1.754 0l6.933 6.928a1.25 1.25 0 0 1 0 1.755L10.417 30.271z'/>
<path d='M29.032 30.271H10.417m6.205-23.58l8.687 8.687'/>
</svg>

After

Width:  |  Height:  |  Size: 431 B

3
src/assets/svg/power.svg Executable file
View File

@@ -0,0 +1,3 @@
<svg xmlns='http://www.w3.org/2000/svg' fill='none' stroke='#fff' fill-rule='evenodd' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' viewBox='0 0 32 32'>
<path d='M16 2.445v12.91m7.746-11.619C27.631 6.27 30.2 10.37 30.2 15.355c0 7.79-6.41 14.2-14.2 14.2s-14.2-6.41-14.2-14.2c0-4.985 2.569-9.085 6.454-11.619'/>
</svg>

After

Width:  |  Height:  |  Size: 339 B

0
src/assets/svg/question.svg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 421 B

After

Width:  |  Height:  |  Size: 421 B

2
src/assets/svg/refresh.svg Normal file → Executable file
View File

@@ -1,3 +1,3 @@
<svg xmlns='http://www.w3.org/2000/svg' fill='none' stroke='#fff' fill-rule='evenodd' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' viewBox='0 0 32 32'> <svg xmlns='http://www.w3.org/2000/svg' fill='none' stroke='#fff' fill-rule='evenodd' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' viewBox='0 0 32 32'>
<path d="M23.247 12.377h7.247V5.13"/><path d="M23.911 25.663a13.29 13.29 0 0 1-9.119 3.623C7.504 29.286 1.506 23.289 1.506 16S7.504 2.713 14.792 2.713a13.29 13.29 0 0 1 9.395 3.891l6.307 5.772"/> <path d='M23.247 12.377h7.247V5.13'/><path d='M23.911 25.663a13.29 13.29 0 0 1-9.119 3.623C7.504 29.286 1.506 23.289 1.506 16S7.504 2.713 14.792 2.713a13.29 13.29 0 0 1 9.395 3.891l6.307 5.772'/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 378 B

After

Width:  |  Height:  |  Size: 378 B

0
src/assets/svg/remote-play.svg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1,4 @@
<svg xmlns='http://www.w3.org/2000/svg' fill='none' stroke='#fff' fill-rule='evenodd' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' viewBox='0 0 32 32'>
<path d='M8.964 21.417h-6.5a1.09 1.09 0 0 1-1.083-1.083v-8.667a1.09 1.09 0 0 1 1.083-1.083h6.5L18.714 3v26l-9.75-7.583z'/>
<path d='M8.964 10.583v10.833m15.167-8.28a4.35 4.35 0 0 1 0 5.728M28.149 9.5a9.79 9.79 0 0 1 0 13'/>
</svg>

After

Width:  |  Height:  |  Size: 410 B

View File

@@ -0,0 +1,3 @@
<svg xmlns='http://www.w3.org/2000/svg' fill='#fff' stroke='none' fill-rule='evenodd' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' viewBox='0 0 32 32'>
<path d='M5.462 3.4c-.205-.23-.499-.363-.808-.363-.592 0-1.079.488-1.079 1.08a1.08 1.08 0 0 0 .289.736l4.247 4.672H2.504a2.17 2.17 0 0 0-2.16 2.16v8.637a2.17 2.17 0 0 0 2.16 2.16h6.107l9.426 7.33a1.08 1.08 0 0 0 .662.227c.592 0 1.08-.487 1.08-1.079v-6.601l5.679 6.247a1.08 1.08 0 0 0 .808.363c.592 0 1.08-.487 1.08-1.079a1.08 1.08 0 0 0-.29-.736L5.462 3.4zm-2.958 8.285h5.398v8.637H2.504v-8.637zM17.62 26.752l-7.558-5.878V11.67l7.558 8.313v6.769zm5.668-8.607c1.072-1.218 1.072-3.063 0-4.281a1.08 1.08 0 0 1-.293-.74c0-.592.487-1.079 1.079-1.079a1.08 1.08 0 0 1 .834.393 5.42 5.42 0 0 1 0 7.137 1.08 1.08 0 0 1-.81.365c-.593 0-1.08-.488-1.08-1.08 0-.263.096-.517.27-.715zM12.469 7.888c-.147-.19-.228-.423-.228-.663a1.08 1.08 0 0 1 .417-.853l5.379-4.184a1.08 1.08 0 0 1 .662-.227c.593 0 1.08.488 1.08 1.08v10.105c0 .593-.487 1.08-1.08 1.08s-1.079-.487-1.079-1.08V5.255l-3.636 2.834c-.469.362-1.153.273-1.515-.196v-.005zm19.187 8.115a10.79 10.79 0 0 1-2.749 7.199 1.08 1.08 0 0 1-.793.347c-.593 0-1.08-.487-1.08-1.079 0-.26.094-.511.264-.708 2.918-3.262 2.918-8.253 0-11.516-.184-.2-.287-.461-.287-.733 0-.592.487-1.08 1.08-1.08a1.08 1.08 0 0 1 .816.373 10.78 10.78 0 0 1 2.749 7.197z' fill-rule='nonzero'/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

0
src/assets/svg/stream-settings.svg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 768 B

After

Width:  |  Height:  |  Size: 768 B

0
src/assets/svg/stream-stats.svg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 430 B

After

Width:  |  Height:  |  Size: 430 B

14
src/assets/svg/touch-control-disable.svg Normal file → Executable file
View File

@@ -1,9 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="#fff" viewBox="0 0 32 32" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2"> <svg xmlns='http://www.w3.org/2000/svg' fill='#fff' viewBox='0 0 32 32' fill-rule='evenodd' stroke-linejoin='round' stroke-miterlimit='2'>
<g fill="none" stroke="#fff"> <g fill='none' stroke='#fff'>
<path d="M6.021 5.021l20 22" stroke-width="2"/> <path d='M6.021 5.021l20 22' stroke-width='2'/>
<path d="M8.735 8.559H2.909a.89.89 0 0 0-.889.889v13.146a.89.89 0 0 0 .889.888h19.34m4.289 0h2.594a.89.89 0 0 0 .889-.888V9.448a.89.89 0 0 0-.889-.889H12.971" stroke-miterlimit="1.5" stroke-width="2.083"/> <path d='M8.735 8.559H2.909a.89.89 0 0 0-.889.889v13.146a.89.89 0 0 0 .889.888h19.34m4.289 0h2.594a.89.89 0 0 0 .889-.888V9.448a.89.89 0 0 0-.889-.889H12.971' stroke-miterlimit='1.5' stroke-width='2.083'/>
</g> </g>
<path d="M8.147 11.981l-.053-.001-.054.001c-.55.028-.988.483-.988 1.04v6c0 .575.467 1.042 1.042 1.042l.053-.001c.55-.028.988-.484.988-1.04v-6a1.04 1.04 0 0 0-.988-1.04z"/> <path d='M8.147 11.981l-.053-.001-.054.001c-.55.028-.988.483-.988 1.04v6c0 .575.467 1.042 1.042 1.042l.053-.001c.55-.028.988-.484.988-1.04v-6a1.04 1.04 0 0 0-.988-1.04z'/>
<path d="M11.147 14.981l-.054-.001h-6a1.04 1.04 0 1 0 0 2.083h6c.575 0 1.042-.467 1.042-1.042a1.04 1.04 0 0 0-.988-1.04z"/> <path d='M11.147 14.981l-.054-.001h-6a1.04 1.04 0 1 0 0 2.083h6c.575 0 1.042-.467 1.042-1.042a1.04 1.04 0 0 0-.988-1.04z'/>
<circle cx="25.345" cy="18.582" r="2.561" fill="none" stroke="#fff" stroke-width="1.78" transform="matrix(1.17131 0 0 1.17131 -5.74235 -5.74456)"/> <circle cx='25.345' cy='18.582' r='2.561' fill='none' stroke='#fff' stroke-width='1.78' transform='matrix(1.17131 0 0 1.17131 -5.74235 -5.74456)'/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 915 B

After

Width:  |  Height:  |  Size: 915 B

10
src/assets/svg/touch-control-enable.svg Normal file → Executable file
View File

@@ -1,6 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="#fff" viewBox="0 0 32 32" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2"> <svg xmlns='http://www.w3.org/2000/svg' fill='#fff' viewBox='0 0 32 32' fill-rule='evenodd' stroke-linejoin='round' stroke-miterlimit='2'>
<path d="M30.021 9.448a.89.89 0 0 0-.889-.889H2.909a.89.89 0 0 0-.889.889v13.146a.89.89 0 0 0 .889.888h26.223a.89.89 0 0 0 .889-.888V9.448z" fill="none" stroke="#fff" stroke-width="2.083"/> <path d='M30.021 9.448a.89.89 0 0 0-.889-.889H2.909a.89.89 0 0 0-.889.889v13.146a.89.89 0 0 0 .889.888h26.223a.89.89 0 0 0 .889-.888V9.448z' fill='none' stroke='#fff' stroke-width='2.083'/>
<path d="M8.147 11.981l-.053-.001-.054.001c-.55.028-.988.483-.988 1.04v6c0 .575.467 1.042 1.042 1.042l.053-.001c.55-.028.988-.484.988-1.04v-6a1.04 1.04 0 0 0-.988-1.04z"/> <path d='M8.147 11.981l-.053-.001-.054.001c-.55.028-.988.483-.988 1.04v6c0 .575.467 1.042 1.042 1.042l.053-.001c.55-.028.988-.484.988-1.04v-6a1.04 1.04 0 0 0-.988-1.04z'/>
<path d="M11.147 14.981l-.054-.001h-6a1.04 1.04 0 1 0 0 2.083h6c.575 0 1.042-.467 1.042-1.042a1.04 1.04 0 0 0-.988-1.04z"/> <path d='M11.147 14.981l-.054-.001h-6a1.04 1.04 0 1 0 0 2.083h6c.575 0 1.042-.467 1.042-1.042a1.04 1.04 0 0 0-.988-1.04z'/>
<circle cx="25.345" cy="18.582" r="2.561" fill="none" stroke="#fff" stroke-width="1.78" transform="matrix(1.17131 0 0 1.17131 -5.74235 -5.74456)"/> <circle cx='25.345' cy='18.582' r='2.561' fill='none' stroke='#fff' stroke-width='1.78' transform='matrix(1.17131 0 0 1.17131 -5.74235 -5.74456)'/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 796 B

After

Width:  |  Height:  |  Size: 796 B

0
src/assets/svg/trash.svg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 433 B

After

Width:  |  Height:  |  Size: 433 B

View File

@@ -0,0 +1,3 @@
<svg xmlns='http://www.w3.org/2000/svg' fill='#fff' stroke='nons' fill-rule='evenodd' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' viewBox='0 0 32 32'>
<path d='M2.497 14.127c.781-6.01 5.542-10.849 11.551-11.708V0C6.634.858.858 6.712 0 14.127h2.497zM17.952 2.419V0C25.366.858 31.142 6.712 32 14.127h-2.497c-.781-6.01-5.542-10.849-11.551-11.708zM2.497 17.873c.781 6.01 5.542 10.849 11.551 11.708V32C6.634 31.142.858 25.288 0 17.873h2.497zm27.006 0H32C31.142 25.288 25.366 31.142 17.952 32v-2.419c6.009-.859 10.77-5.698 11.551-11.708zm-19.2-4.527h2.028a.702.702 0 1 0 0-1.404h-2.107a1.37 1.37 0 0 1-1.326-1.327V9.21a.7.7 0 0 0-.703-.703c-.387 0-.703.316-.703.7v1.408c.079 1.483 1.25 2.731 2.811 2.731zm2.809 7.337h-2.888a1.37 1.37 0 0 1-1.326-1.327v-4.917c0-.387-.316-.703-.7-.703a.7.7 0 0 0-.706.703v4.917a2.77 2.77 0 0 0 2.732 2.732h2.81c.387 0 .702-.316.702-.7.078-.393-.234-.705-.624-.705zM25.6 19.2a.7.7 0 0 0-.702-.702c-.387 0-.703.316-.703.699v.081c0 .702-.546 1.326-1.248 1.326H19.98c-.702-.078-1.248-.624-1.248-1.326v-.312c0-.78.624-1.327 1.326-1.327h2.811a2.77 2.77 0 0 0 2.731-2.732v-.312a2.68 2.68 0 0 0-2.576-2.732h-4.76a.702.702 0 1 0 0 1.405h4.526a1.37 1.37 0 0 1 1.327 1.327v.234c0 .781-.624 1.327-1.327 1.327h-2.81a2.77 2.77 0 0 0-2.731 2.732v.312a2.77 2.77 0 0 0 2.731 2.732h2.967a2.74 2.74 0 0 0 2.575-2.732s.078.078.078 0z'/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

4
src/assets/svg/upload.svg Executable file
View File

@@ -0,0 +1,4 @@
<svg xmlns='http://www.w3.org/2000/svg' fill='none' stroke='#fff' fill-rule='evenodd' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' viewBox='0 0 32 32'>
<path d='M16 19.905V1.682m14.318 18.223v9.112a1.31 1.31 0 0 1-1.302 1.302H2.983a1.31 1.31 0 0 1-1.302-1.302v-9.112'/>
<path d='M9.492 8.19L16 1.682l6.508 6.508'/>
</svg>

After

Width:  |  Height:  |  Size: 349 B

View File

@@ -0,0 +1,11 @@
<svg xmlns='http://www.w3.org/2000/svg' fill='none' stroke='#fff' fill-rule='evenodd' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' viewBox='0 0 32 32'>
<g stroke-width='2.06'>
<path d='M8.417 13.218h4.124'/>
<path d='M10.479 11.155v4.125'/>
<path d='M12.787 19.404L7.36 25.565a3.61 3.61 0 0 1-2.551 1.056A3.63 3.63 0 0 1 1.2 23.013c0-.21.018-.42.055-.626l2.108-10.845C3.923 8.356 6.714 6.007 9.949 6h5.192'/>
</g>
<g stroke-width='2.11'>
<path d='M30.8 13.1c0-3.919-3.181-7.1-7.1-7.1s-7.1 3.181-7.1 7.1v6.421c0 3.919 3.181 7.1 7.1 7.1s7.1-3.181 7.1-7.1V13.1z'/>
<path d='M23.7 14.724V9.966'/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 680 B

0
src/build-config.ts Normal file → Executable file
View File

17
src/enums/bypass-servers.ts Executable file
View File

@@ -0,0 +1,17 @@
import { t } from "@/utils/translation"
export const BypassServers = {
br: t('brazil'),
jp: t('japan'),
kr: t('korea'),
pl: t('poland'),
us: t('united-states'),
} as const;
export const BypassServerIps: Record<keyof typeof BypassServers, string> = {
br: '169.150.198.66',
kr: '121.125.60.151',
jp: '138.199.21.239',
pl: '45.134.212.66',
us: '143.244.47.65',
} as const;

9
src/enums/game-pass-gallery.ts Executable file
View File

@@ -0,0 +1,9 @@
export enum GamePassCloudGallery {
ALL = '29a81209-df6f-41fd-a528-2ae6b91f719c',
ALL_WITH_BYGO = 'ce573635-7c18-4d0c-9d68-90b932393470',
LEAVING_SOON = '393f05bf-e596-4ef6-9487-6d4fa0eab987',
MOST_POPULAR = 'e7590b22-e299-44db-ae22-25c61405454c',
NATIVE_MKB = '8fa264dd-124f-4af3-97e8-596fcdf4b486',
RECENTLY_ADDED = '44a55037-770f-4bbf-bde5-a9fa27dba1da',
TOUCH = '9c86f07a-f3e8-45ad-82a0-a1f759597059',
}

41
src/modules/mkb/definitions.ts → src/enums/gamepad.ts Normal file → Executable file
View File

@@ -1,5 +1,4 @@
import type { GamepadKeyNameType } from "@/types/mkb"; import { PrompFont } from "./prompt-font";
import { PrompFont } from "@/utils/prompt-font";
export enum GamepadKey { export enum GamepadKey {
A = 0, A = 0,
@@ -25,15 +24,16 @@ export enum GamepadKey {
LS_DOWN = 101, LS_DOWN = 101,
LS_LEFT = 102, LS_LEFT = 102,
LS_RIGHT = 103, LS_RIGHT = 103,
LS = 104,
RS_UP = 200, RS_UP = 200,
RS_DOWN = 201, RS_DOWN = 201,
RS_LEFT = 202, RS_LEFT = 202,
RS_RIGHT = 203, RS_RIGHT = 203,
RS = 204,
}; };
export const GamepadKeyName: Record<number, [string, PrompFont]> = {
export const GamepadKeyName: GamepadKeyNameType = {
[GamepadKey.A]: ['A', PrompFont.A], [GamepadKey.A]: ['A', PrompFont.A],
[GamepadKey.B]: ['B', PrompFont.B], [GamepadKey.B]: ['B', PrompFont.B],
[GamepadKey.X]: ['X', PrompFont.X], [GamepadKey.X]: ['X', PrompFont.X],
@@ -58,12 +58,16 @@ export const GamepadKeyName: GamepadKeyNameType = {
[GamepadKey.LS_DOWN]: ['Left Stick Down', PrompFont.LS_DOWN], [GamepadKey.LS_DOWN]: ['Left Stick Down', PrompFont.LS_DOWN],
[GamepadKey.LS_LEFT]: ['Left Stick Left', PrompFont.LS_LEFT], [GamepadKey.LS_LEFT]: ['Left Stick Left', PrompFont.LS_LEFT],
[GamepadKey.LS_RIGHT]: ['Left Stick Right', PrompFont.LS_RIGHT], [GamepadKey.LS_RIGHT]: ['Left Stick Right', PrompFont.LS_RIGHT],
[GamepadKey.LS]: ['Left Stick', PrompFont.LS],
[GamepadKey.R3]: ['R3', PrompFont.R3], [GamepadKey.R3]: ['R3', PrompFont.R3],
[GamepadKey.RS_UP]: ['Right Stick Up', PrompFont.RS_UP], [GamepadKey.RS_UP]: ['Right Stick Up', PrompFont.RS_UP],
[GamepadKey.RS_DOWN]: ['Right Stick Down', PrompFont.RS_DOWN], [GamepadKey.RS_DOWN]: ['Right Stick Down', PrompFont.RS_DOWN],
[GamepadKey.RS_LEFT]: ['Right Stick Left', PrompFont.RS_LEFT], [GamepadKey.RS_LEFT]: ['Right Stick Left', PrompFont.RS_LEFT],
[GamepadKey.RS_RIGHT]: ['Right Stick Right', PrompFont.RS_RIGHT], [GamepadKey.RS_RIGHT]: ['Right Stick Right', PrompFont.RS_RIGHT],
[GamepadKey.RS]: ['Right Stick', PrompFont.RS],
[GamepadKey.SHARE]: ['Screenshot', PrompFont.SHARE],
}; };
@@ -71,32 +75,3 @@ export enum GamepadStick {
LEFT = 0, LEFT = 0,
RIGHT = 1, RIGHT = 1,
}; };
export enum MouseButtonCode {
LEFT_CLICK = 'Mouse0',
RIGHT_CLICK = 'Mouse2',
MIDDLE_CLICK = 'Mouse1',
};
export enum MouseMapTo {
OFF = 0,
LS = 1,
RS = 2,
}
export enum WheelCode {
SCROLL_UP = 'ScrollUp',
SCROLL_DOWN = 'ScrollDown',
SCROLL_LEFT = 'ScrollLeft',
SCROLL_RIGHT = 'ScrollRight',
};
export enum MkbPresetKey {
MOUSE_MAP_TO = 'map_to',
MOUSE_SENSITIVITY_X = 'sensitivity_x',
MOUSE_SENSITIVITY_Y = 'sensitivity_y',
MOUSE_DEADZONE_COUNTERWEIGHT = 'deadzone_counterweight',
}

44
src/enums/mkb.ts Executable file
View File

@@ -0,0 +1,44 @@
export const enum MouseConstant {
DEFAULT_PANNING_SENSITIVITY = 0.0010,
DEFAULT_DEADZONE_COUNTERWEIGHT = 0.01,
MAXIMUM_STICK_RANGE = 1.1,
}
export const enum MouseButtonCode {
LEFT_CLICK = 'Mouse0',
RIGHT_CLICK = 'Mouse2',
MIDDLE_CLICK = 'Mouse1',
};
export const enum MouseMapTo {
OFF = 0,
LS = 1,
RS = 2,
}
export const enum WheelCode {
SCROLL_UP = 'ScrollUp',
SCROLL_DOWN = 'ScrollDown',
SCROLL_LEFT = 'ScrollLeft',
SCROLL_RIGHT = 'ScrollRight',
};
export const enum MkbPresetKey {
MOUSE_MAP_TO = 'mapTo',
MOUSE_SENSITIVITY_X = 'sensitivityX',
MOUSE_SENSITIVITY_Y = 'sensitivityY',
MOUSE_DEADZONE_COUNTERWEIGHT = 'deadzoneCounterweight',
}
export const enum KeyModifier {
CTRL = 1,
ALT = 2,
SHIFT = 4,
}

313
src/enums/pref-keys.ts Executable file
View File

@@ -0,0 +1,313 @@
import type { BaseSettingsStorage } from "@/utils/settings-storages/base-settings-storage";
import type { BlockFeature, CodecProfile, DeviceVibrationMode, GameBarPosition, LoadingScreenRocket, NativeMkbMode, StreamPlayerType, StreamResolution, StreamStat, StreamStatPosition, StreamVideoProcessing, TouchControllerMode, TouchControllerStyleCustom, TouchControllerStyleStandard, UiLayout, UiSection, UiTheme, VideoPosition, VideoPowerPreference, VideoRatio } from "./pref-values"
export const enum StorageKey {
GLOBAL = 'BetterXcloud',
STREAM = 'BetterXcloud.Stream',
LOCALE = 'BetterXcloud.Locale',
LOCALE_TRANSLATIONS = 'BetterXcloud.Locale.Translations',
PATCHES_CACHE = 'BetterXcloud.Patches.Cache',
PATCHES_SIGNATURE = 'BetterXcloud.Patches.Cache.Signature',
USER_AGENT = 'BetterXcloud.UserAgent',
GH_PAGES_COMMIT_HASH = 'BetterXcloud.GhPages.CommitHash',
LIST_CUSTOM_TOUCH_LAYOUTS = 'BetterXcloud.GhPages.CustomTouchLayouts',
LIST_FORCE_NATIVE_MKB = 'BetterXcloud.GhPages.ForceNativeMkb',
LIST_LOCAL_CO_OP = 'BetterXcloud.GhPages.LocalCoOp',
}
export const enum GlobalPref {
VERSION_LAST_CHECK = 'version.lastCheck',
VERSION_LATEST = 'version.latest',
VERSION_CURRENT = 'version.current',
SCRIPT_LOCALE = 'bx.locale',
SERVER_REGION = 'server.region',
SERVER_BYPASS_RESTRICTION = 'server.bypassRestriction',
SERVER_PREFER_IPV6 = 'server.ipv6.prefer',
STREAM_PREFERRED_LOCALE = 'stream.locale',
STREAM_RESOLUTION = 'stream.video.resolution',
STREAM_CODEC_PROFILE = 'stream.video.codecProfile',
STREAM_MAX_VIDEO_BITRATE = 'stream.video.maxBitrate',
STREAM_COMBINE_SOURCES = 'stream.video.combineAudio',
USER_AGENT_PROFILE = 'userAgent.profile',
TOUCH_CONTROLLER_MODE = 'touchController.mode',
TOUCH_CONTROLLER_AUTO_OFF = 'touchController.autoOff',
TOUCH_CONTROLLER_DEFAULT_OPACITY = 'touchController.opacity.default',
TOUCH_CONTROLLER_STYLE_STANDARD = 'touchController.style.standard',
TOUCH_CONTROLLER_STYLE_CUSTOM = 'touchController.style.custom',
GAME_BAR_POSITION = 'gameBar.position',
NATIVE_MKB_MODE = 'nativeMkb.mode',
NATIVE_MKB_FORCED_GAMES = 'nativeMkb.forcedGames',
MKB_ENABLED = 'mkb.enabled',
MKB_HIDE_IDLE_CURSOR = 'mkb.cursor.hideIdle',
SCREENSHOT_APPLY_FILTERS = 'screenshot.applyFilters',
BLOCK_TRACKING = 'block.tracking',
BLOCK_FEATURES = 'block.features',
LOADING_SCREEN_GAME_ART = 'loadingScreen.gameArt.show',
LOADING_SCREEN_SHOW_WAIT_TIME = 'loadingScreen.waitTime.show',
LOADING_SCREEN_ROCKET = 'loadingScreen.rocket',
UI_CONTROLLER_FRIENDLY = 'ui.controllerFriendly',
UI_LAYOUT = 'ui.layout',
UI_SCROLLBAR_HIDE = 'ui.hideScrollbar',
UI_HIDE_SECTIONS = 'ui.hideSections',
UI_GAME_CARD_SHOW_WAIT_TIME = 'ui.gameCard.waitTime.show',
UI_SIMPLIFY_STREAM_MENU = 'ui.streamMenu.simplify',
UI_DISABLE_FEEDBACK_DIALOG = 'ui.feedbackDialog.disabled',
UI_CONTROLLER_SHOW_STATUS = 'ui.controllerStatus.show',
UI_SKIP_SPLASH_VIDEO = 'ui.splashVideo.skip',
UI_HIDE_SYSTEM_MENU_ICON = 'ui.systemMenu.hideHandle',
UI_REDUCE_ANIMATIONS = 'ui.reduceAnimations',
UI_IMAGE_QUALITY = 'ui.imageQuality',
UI_THEME = 'ui.theme',
AUDIO_MIC_ON_PLAYING = 'audio.mic.onPlaying',
AUDIO_VOLUME_CONTROL_ENABLED = 'audio.volume.booster.enabled',
REMOTE_PLAY_ENABLED = 'xhome.enabled',
REMOTE_PLAY_STREAM_RESOLUTION = 'xhome.video.resolution',
GAME_FORTNITE_FORCE_CONSOLE = 'game.fortnite.forceConsole',
}
export type GlobalPrefTypeMap = {
[GlobalPref.AUDIO_MIC_ON_PLAYING]: boolean;
[GlobalPref.AUDIO_VOLUME_CONTROL_ENABLED]: boolean;
[GlobalPref.BLOCK_FEATURES]: BlockFeature[];
[GlobalPref.BLOCK_TRACKING]: boolean;
[GlobalPref.GAME_BAR_POSITION]: GameBarPosition;
[GlobalPref.GAME_FORTNITE_FORCE_CONSOLE]: boolean;
[GlobalPref.LOADING_SCREEN_GAME_ART]: boolean;
[GlobalPref.LOADING_SCREEN_ROCKET]: LoadingScreenRocket;
[GlobalPref.LOADING_SCREEN_SHOW_WAIT_TIME]: boolean;
[GlobalPref.MKB_ENABLED]: boolean;
[GlobalPref.MKB_HIDE_IDLE_CURSOR]: boolean;
[GlobalPref.NATIVE_MKB_FORCED_GAMES]: string[];
[GlobalPref.NATIVE_MKB_MODE]: NativeMkbMode;
[GlobalPref.REMOTE_PLAY_ENABLED]: boolean;
[GlobalPref.REMOTE_PLAY_STREAM_RESOLUTION]: StreamResolution;
[GlobalPref.SCREENSHOT_APPLY_FILTERS]: boolean;
[GlobalPref.SERVER_BYPASS_RESTRICTION]: string;
[GlobalPref.SERVER_PREFER_IPV6]: boolean;
[GlobalPref.SERVER_REGION]: string;
[GlobalPref.STREAM_CODEC_PROFILE]: CodecProfile;
[GlobalPref.STREAM_COMBINE_SOURCES]: boolean;
[GlobalPref.STREAM_MAX_VIDEO_BITRATE]: number;
[GlobalPref.STREAM_PREFERRED_LOCALE]: StreamPreferredLocale;
[GlobalPref.STREAM_RESOLUTION]: StreamResolution;
[GlobalPref.TOUCH_CONTROLLER_AUTO_OFF]: boolean;
[GlobalPref.TOUCH_CONTROLLER_DEFAULT_OPACITY]: number;
[GlobalPref.TOUCH_CONTROLLER_MODE]: TouchControllerMode;
[GlobalPref.TOUCH_CONTROLLER_STYLE_CUSTOM]: TouchControllerStyleCustom;
[GlobalPref.TOUCH_CONTROLLER_STYLE_STANDARD]: TouchControllerStyleStandard;
[GlobalPref.UI_CONTROLLER_FRIENDLY]: boolean;
[GlobalPref.UI_CONTROLLER_SHOW_STATUS]: boolean;
[GlobalPref.UI_DISABLE_FEEDBACK_DIALOG]: boolean;
[GlobalPref.UI_GAME_CARD_SHOW_WAIT_TIME]: boolean;
[GlobalPref.UI_HIDE_SECTIONS]: UiSection[];
[GlobalPref.UI_HIDE_SYSTEM_MENU_ICON]: boolean;
[GlobalPref.UI_IMAGE_QUALITY]: number;
[GlobalPref.UI_LAYOUT]: UiLayout;
[GlobalPref.UI_REDUCE_ANIMATIONS]: boolean;
[GlobalPref.UI_SCROLLBAR_HIDE]: boolean;
[GlobalPref.UI_SIMPLIFY_STREAM_MENU]: boolean;
[GlobalPref.UI_SKIP_SPLASH_VIDEO]: boolean;
[GlobalPref.UI_THEME]: UiTheme;
[GlobalPref.VERSION_CURRENT]: string;
[GlobalPref.VERSION_LAST_CHECK]: number;
[GlobalPref.VERSION_LATEST]: string;
[GlobalPref.SCRIPT_LOCALE]: string;
[GlobalPref.USER_AGENT_PROFILE]: string;
}
export const enum StreamPref {
LOCAL_CO_OP_ENABLED = 'localCoOp.enabled',
DEVICE_VIBRATION_MODE = 'deviceVibration.mode',
DEVICE_VIBRATION_INTENSITY = 'deviceVibration.intensity',
CONTROLLER_POLLING_RATE = 'controller.pollingRate',
CONTROLLER_SETTINGS = 'controller.settings',
NATIVE_MKB_SCROLL_HORIZONTAL_SENSITIVITY = 'nativeMkb.scroll.sensitivityX',
NATIVE_MKB_SCROLL_VERTICAL_SENSITIVITY = 'nativeMkb.scroll.sensitivityY',
MKB_P1_MAPPING_PRESET_ID = 'mkb.p1.preset.mappingId',
MKB_P1_SLOT = 'mkb.p1.slot',
MKB_P2_MAPPING_PRESET_ID = 'mkb.p2.preset.mappingId',
MKB_P2_SLOT = 'mkb.p2.slot',
KEYBOARD_SHORTCUTS_IN_GAME_PRESET_ID = 'keyboardShortcuts.preset.inGameId',
VIDEO_PLAYER_TYPE = 'video.player.type',
VIDEO_POWER_PREFERENCE = 'video.player.powerPreference',
VIDEO_PROCESSING = 'video.processing',
VIDEO_SHARPNESS = 'video.processing.sharpness',
VIDEO_MAX_FPS = 'video.maxFps',
VIDEO_RATIO = 'video.ratio',
VIDEO_BRIGHTNESS = 'video.brightness',
VIDEO_CONTRAST = 'video.contrast',
VIDEO_SATURATION = 'video.saturation',
VIDEO_POSITION = 'video.position',
AUDIO_VOLUME = 'audio.volume',
STATS_ITEMS = 'stats.items',
STATS_SHOW_WHEN_PLAYING = 'stats.showWhenPlaying',
STATS_QUICK_GLANCE_ENABLED = 'stats.quickGlance.enabled',
STATS_POSITION = 'stats.position',
STATS_TEXT_SIZE = 'stats.textSize',
STATS_OPACITY_ALL = 'stats.opacity.all',
STATS_OPACITY_BACKGROUND = 'stats.opacity.background',
STATS_CONDITIONAL_FORMATTING = 'stats.colors',
}
export type StreamPrefTypeMap = {
[StreamPref.AUDIO_VOLUME]: number;
[StreamPref.CONTROLLER_POLLING_RATE]: number;
[StreamPref.CONTROLLER_SETTINGS]: ControllerSettings;
[StreamPref.DEVICE_VIBRATION_INTENSITY]: number;
[StreamPref.DEVICE_VIBRATION_MODE]: DeviceVibrationMode;
[StreamPref.KEYBOARD_SHORTCUTS_IN_GAME_PRESET_ID]: number;
[StreamPref.LOCAL_CO_OP_ENABLED]: boolean;
[StreamPref.MKB_P1_MAPPING_PRESET_ID]: number;
[StreamPref.MKB_P1_SLOT]: number;
[StreamPref.MKB_P2_MAPPING_PRESET_ID]: number;
[StreamPref.MKB_P2_SLOT]: number;
[StreamPref.NATIVE_MKB_SCROLL_HORIZONTAL_SENSITIVITY]: number;
[StreamPref.NATIVE_MKB_SCROLL_VERTICAL_SENSITIVITY]: number;
[StreamPref.STATS_CONDITIONAL_FORMATTING]: boolean;
[StreamPref.STATS_ITEMS]: StreamStat[];
[StreamPref.STATS_OPACITY_ALL]: number;
[StreamPref.STATS_OPACITY_BACKGROUND]: number;
[StreamPref.STATS_POSITION]: StreamStatPosition;
[StreamPref.STATS_QUICK_GLANCE_ENABLED]: boolean;
[StreamPref.STATS_SHOW_WHEN_PLAYING]: boolean;
[StreamPref.STATS_TEXT_SIZE]: string;
[StreamPref.VIDEO_BRIGHTNESS]: number;
[StreamPref.VIDEO_CONTRAST]: number;
[StreamPref.VIDEO_MAX_FPS]: number;
[StreamPref.VIDEO_PLAYER_TYPE]: StreamPlayerType;
[StreamPref.VIDEO_POSITION]: VideoPosition;
[StreamPref.VIDEO_POWER_PREFERENCE]: VideoPowerPreference;
[StreamPref.VIDEO_PROCESSING]: StreamVideoProcessing;
[StreamPref.VIDEO_RATIO]: VideoRatio;
[StreamPref.VIDEO_SATURATION]: number;
[StreamPref.VIDEO_SHARPNESS]: number;
}
export type AllPrefs = GlobalPref | StreamPref;
export const ALL_PREFS: {
global: GlobalPref[],
stream: StreamPref[],
} = {
global: [
GlobalPref.AUDIO_MIC_ON_PLAYING,
GlobalPref.AUDIO_VOLUME_CONTROL_ENABLED,
GlobalPref.BLOCK_FEATURES,
GlobalPref.BLOCK_TRACKING,
GlobalPref.GAME_BAR_POSITION,
GlobalPref.GAME_FORTNITE_FORCE_CONSOLE,
GlobalPref.LOADING_SCREEN_GAME_ART,
GlobalPref.LOADING_SCREEN_ROCKET,
GlobalPref.LOADING_SCREEN_SHOW_WAIT_TIME,
GlobalPref.MKB_ENABLED,
GlobalPref.MKB_HIDE_IDLE_CURSOR,
GlobalPref.NATIVE_MKB_FORCED_GAMES,
GlobalPref.NATIVE_MKB_MODE,
GlobalPref.REMOTE_PLAY_ENABLED,
GlobalPref.REMOTE_PLAY_STREAM_RESOLUTION,
GlobalPref.SCREENSHOT_APPLY_FILTERS,
GlobalPref.SERVER_BYPASS_RESTRICTION,
GlobalPref.SERVER_PREFER_IPV6,
GlobalPref.SERVER_REGION,
GlobalPref.STREAM_CODEC_PROFILE,
GlobalPref.STREAM_COMBINE_SOURCES,
GlobalPref.STREAM_MAX_VIDEO_BITRATE,
GlobalPref.STREAM_PREFERRED_LOCALE,
GlobalPref.STREAM_RESOLUTION,
GlobalPref.TOUCH_CONTROLLER_AUTO_OFF,
GlobalPref.TOUCH_CONTROLLER_DEFAULT_OPACITY,
GlobalPref.TOUCH_CONTROLLER_MODE,
GlobalPref.TOUCH_CONTROLLER_STYLE_CUSTOM,
GlobalPref.TOUCH_CONTROLLER_STYLE_STANDARD,
GlobalPref.UI_CONTROLLER_FRIENDLY,
GlobalPref.UI_CONTROLLER_SHOW_STATUS,
GlobalPref.UI_DISABLE_FEEDBACK_DIALOG,
GlobalPref.UI_GAME_CARD_SHOW_WAIT_TIME,
GlobalPref.UI_HIDE_SECTIONS,
GlobalPref.UI_HIDE_SYSTEM_MENU_ICON,
GlobalPref.UI_IMAGE_QUALITY,
GlobalPref.UI_LAYOUT,
GlobalPref.UI_REDUCE_ANIMATIONS,
GlobalPref.UI_SCROLLBAR_HIDE,
GlobalPref.UI_SIMPLIFY_STREAM_MENU,
GlobalPref.UI_SKIP_SPLASH_VIDEO,
GlobalPref.UI_THEME,
GlobalPref.VERSION_CURRENT,
GlobalPref.VERSION_LAST_CHECK,
GlobalPref.VERSION_LATEST,
GlobalPref.SCRIPT_LOCALE,
GlobalPref.USER_AGENT_PROFILE,
],
stream: [
StreamPref.AUDIO_VOLUME,
StreamPref.CONTROLLER_POLLING_RATE,
StreamPref.CONTROLLER_SETTINGS,
StreamPref.DEVICE_VIBRATION_INTENSITY,
StreamPref.DEVICE_VIBRATION_MODE,
StreamPref.KEYBOARD_SHORTCUTS_IN_GAME_PRESET_ID,
StreamPref.LOCAL_CO_OP_ENABLED,
StreamPref.MKB_P1_MAPPING_PRESET_ID,
StreamPref.MKB_P1_SLOT,
StreamPref.MKB_P2_MAPPING_PRESET_ID,
StreamPref.MKB_P2_SLOT,
StreamPref.NATIVE_MKB_SCROLL_HORIZONTAL_SENSITIVITY,
StreamPref.NATIVE_MKB_SCROLL_VERTICAL_SENSITIVITY,
StreamPref.STATS_CONDITIONAL_FORMATTING,
StreamPref.STATS_ITEMS,
StreamPref.STATS_OPACITY_ALL,
StreamPref.STATS_OPACITY_BACKGROUND,
StreamPref.STATS_POSITION,
StreamPref.STATS_QUICK_GLANCE_ENABLED,
StreamPref.STATS_SHOW_WHEN_PLAYING,
StreamPref.STATS_TEXT_SIZE,
StreamPref.VIDEO_BRIGHTNESS,
StreamPref.VIDEO_CONTRAST,
StreamPref.VIDEO_MAX_FPS,
StreamPref.VIDEO_PLAYER_TYPE,
StreamPref.VIDEO_POSITION,
StreamPref.VIDEO_POWER_PREFERENCE,
StreamPref.VIDEO_PROCESSING,
StreamPref.VIDEO_RATIO,
StreamPref.VIDEO_SATURATION,
StreamPref.VIDEO_SHARPNESS,
],
} as const;
export type AnySettingsStorage = BaseSettingsStorage<GlobalPref> | BaseSettingsStorage<StreamPref>;
export type AnyPref = GlobalPref | StreamPref;
export type PrefTypeMap<Key> = Key extends GlobalPref
? GlobalPrefTypeMap
: Key extends StreamPref
? StreamPrefTypeMap
: never;

141
src/enums/pref-values.ts Executable file
View File

@@ -0,0 +1,141 @@
export const enum UiSection {
ALL_GAMES = 'all-games',
FRIENDS = 'friends',
MOST_POPULAR = 'most-popular',
NATIVE_MKB = 'native-mkb',
NEWS = 'news',
TOUCH = 'touch',
BOYG = 'byog',
RECENTLY_ADDED = 'recently-added',
LEAVING_SOON = 'leaving-soon',
GENRES = 'genres',
}
export const enum GameBarPosition {
BOTTOM_LEFT = 'bottom-left',
BOTTOM_RIGHT = 'bottom-right',
OFF = 'off',
};
export const enum UiLayout {
TV = 'tv',
NORMAL = 'normal',
DEFAULT = 'default',
}
export const enum LoadingScreenRocket {
SHOW = 'show',
HIDE = 'hide',
HIDE_QUEUE = 'hide-queue',
}
export const enum StreamResolution {
DIM_720P = '720p',
DIM_1080P = '1080p',
DIM_1080P_HQ = '1080p-hq',
AUTO = 'auto',
}
export const enum CodecProfile {
DEFAULT = 'default',
LOW = 'low',
NORMAL = 'normal',
HIGH = 'high',
};
export const enum TouchControllerMode {
DEFAULT = 'default',
ALL = 'all',
OFF = 'off',
}
export const enum TouchControllerStyleStandard {
DEFAULT = 'default',
WHITE = 'white',
MUTED = 'muted',
}
export const enum TouchControllerStyleCustom {
DEFAULT = 'default',
MUTED = 'muted',
}
export const enum DeviceVibrationMode {
ON = 'on',
AUTO = 'auto',
OFF = 'off',
}
export const enum NativeMkbMode {
DEFAULT = 'default',
ON = 'on',
OFF = 'off',
}
export const enum StreamStat {
PING = 'ping',
JITTER = 'jit',
FPS = 'fps',
BITRATE = 'btr',
DECODE_TIME = 'dt',
PACKETS_LOST = 'pl',
FRAMES_LOST = 'fl',
DOWNLOAD = 'dl',
UPLOAD = 'ul',
PLAYTIME = 'play',
BATTERY = 'batt',
CLOCK = 'time',
};
export const enum StreamStatPosition {
TOP_LEFT = 'top-left',
TOP_CENTER = 'top-center',
TOP_RIGHT = 'top-right',
}
export const enum VideoRatio {
'16:9' = '16:9',
'18:9' = '18:9',
'21:9' = '21:9',
'16:10' = '16:10',
'4:3' = '4:3',
FILL = 'fill',
}
export const enum VideoPosition {
CENTER = 'center',
TOP = 'top',
TOP_HALF = 'top-half',
BOTTOM = 'bottom',
BOTTOM_HALF = 'bottom-half',
}
export const enum VideoPowerPreference {
DEFAULT = 'default',
LOW_POWER = 'low-power',
HIGH_PERFORMANCE = 'high-performance',
}
export const enum StreamPlayerType {
VIDEO = 'default',
WEBGL2 = 'webgl2',
WEBGPU = 'webgpu',
}
export const enum StreamVideoProcessing {
USM = 'usm',
CAS = 'cas',
}
export const enum BlockFeature {
CHAT = 'chat',
FRIENDS = 'friends',
BYOG = 'byog',
NOTIFICATIONS_INVITES = 'notifications-invites',
NOTIFICATIONS_ACHIEVEMENTS = 'notifications-achievements',
}
export const enum UiTheme {
DEFAULT = 'default',
DARK_OLED = 'dark-oled',
}

4
src/utils/prompt-font.ts → src/enums/prompt-font.ts Normal file → Executable file
View File

@@ -18,15 +18,19 @@ export enum PrompFont {
LEFT = '≺', LEFT = '≺',
RIGHT = '≼', RIGHT = '≼',
LS = '⇱',
L3 = '↺', L3 = '↺',
LS_UP = '↾', LS_UP = '↾',
LS_DOWN = '⇂', LS_DOWN = '⇂',
LS_LEFT = '↼', LS_LEFT = '↼',
LS_RIGHT = '⇀', LS_RIGHT = '⇀',
RS = '⇲',
R3 = '↻', R3 = '↻',
RS_UP = '↿', RS_UP = '↿',
RS_DOWN = '⇃', RS_DOWN = '⇃',
RS_LEFT = '↽', RS_LEFT = '↽',
RS_RIGHT = '⇁', RS_RIGHT = '⇁',
SHARE = '⇧',
} }

27
src/enums/shortcut-actions.ts Executable file
View File

@@ -0,0 +1,27 @@
export const enum ShortcutAction {
BETTER_XCLOUD_SETTINGS_SHOW = 'bx.settings.show',
CONTROLLER_XBOX_BUTTON_PRESS = 'controller.xbox.press',
STREAM_VIDEO_TOGGLE = 'stream.video.toggle',
STREAM_SCREENSHOT_CAPTURE = 'stream.screenshot.capture',
STREAM_MENU_SHOW = 'stream.menu.show',
STREAM_STATS_TOGGLE = 'stream.stats.toggle',
STREAM_SOUND_TOGGLE = 'stream.sound.toggle',
STREAM_MICROPHONE_TOGGLE = 'stream.microphone.toggle',
STREAM_VOLUME_INC = 'stream.volume.inc',
STREAM_VOLUME_DEC = 'stream.volume.dec',
DEVICE_SOUND_TOGGLE = 'device.sound.toggle',
DEVICE_VOLUME_INC = 'device.volume.inc',
DEVICE_VOLUME_DEC = 'device.volume.dec',
DEVICE_BRIGHTNESS_INC = 'device.brightness.inc',
DEVICE_BRIGHTNESS_DEC = 'device.brightness.dec',
MKB_TOGGLE = 'mkb.toggle',
TRUE_ACHIEVEMENTS_OPEN = 'ta.open',
};

9
src/enums/user-agent.ts Executable file
View File

@@ -0,0 +1,9 @@
export enum UserAgentProfile {
WINDOWS_EDGE = 'windows-edge',
MACOS_SAFARI = 'macos-safari',
SMART_TV_GENERIC = 'smarttv-generic',
SMART_TV_TIZEN = 'smarttv-tizen',
VR_OCULUS = 'vr-oculus',
DEFAULT = 'default',
CUSTOM = 'custom',
}

489
src/index.ts Normal file → Executable file
View File

@@ -1,3 +1,5 @@
import { compressCss, isFullVersion } from "@macros/build" with { type: "macro" };
import "@utils/global"; import "@utils/global";
import { BxEvent } from "@utils/bx-event"; import { BxEvent } from "@utils/bx-event";
import { BX_FLAGS } from "@utils/bx-flags"; import { BX_FLAGS } from "@utils/bx-flags";
@@ -6,82 +8,147 @@ import { t } from "@utils/translation";
import { interceptHttpRequests } from "@utils/network"; import { interceptHttpRequests } from "@utils/network";
import { CE } from "@utils/html"; import { CE } from "@utils/html";
import { showGamepadToast } from "@utils/gamepad"; import { showGamepadToast } from "@utils/gamepad";
import { MkbHandler } from "@modules/mkb/mkb-handler"; import { EmulatedMkbHandler } from "@modules/mkb/mkb-handler";
import { StreamBadges } from "@modules/stream/stream-badges"; import { StreamBadges } from "@modules/stream/stream-badges";
import { StreamStats } from "@modules/stream/stream-stats"; import { StreamStats } from "@modules/stream/stream-stats";
import { addCss } from "@utils/css"; import { addCss, preloadFonts } from "@utils/css";
import { Toast } from "@utils/toast";
import { setupStreamUi, updateVideoPlayerCss } from "@modules/ui/ui";
import { PrefKey, getPref } from "@utils/preferences";
import { LoadingScreen } from "@modules/loading-screen"; import { LoadingScreen } from "@modules/loading-screen";
import { MouseCursorHider } from "@modules/mkb/mouse-cursor-hider"; import { MouseCursorHider } from "@modules/mkb/mouse-cursor-hider";
import { TouchController } from "@modules/touch-controller"; import { TouchController } from "@modules/touch-controller";
import { watchHeader } from "@modules/ui/header"; import { checkForUpdate, disablePwa, productTitleToSlug } from "@utils/utils";
import { checkForUpdate, disablePwa } from "@utils/utils"; import { Patcher } from "@/modules/patcher/patcher";
import { Patcher } from "@modules/patcher"; import { RemotePlayManager } from "@/modules/remote-play-manager";
import { RemotePlay } from "@modules/remote-play";
import { onHistoryChanged, patchHistoryMethod } from "@utils/history"; import { onHistoryChanged, patchHistoryMethod } from "@utils/history";
import { VibrationManager } from "@modules/vibration-manager"; import { disableAdobeAudienceManager, patchAudioContext, patchCanvasContext, patchMeControl, patchPointerLockApi, patchRtcCodecs, patchRtcPeerConnection, patchVideoApi } from "@utils/monkey-patches";
import { overridePreloadState } from "@utils/preload-state";
import { patchAudioContext, patchCanvasContext, patchMeControl, patchRtcCodecs, patchRtcPeerConnection, patchVideoApi } from "@utils/monkey-patches";
import { AppInterface, STATES } from "@utils/global"; import { AppInterface, STATES } from "@utils/global";
import { injectStreamMenuButtons } from "@modules/stream/stream-ui";
import { BxLogger } from "@utils/bx-logger"; import { BxLogger } from "@utils/bx-logger";
import { GameBar } from "./modules/game-bar/game-bar"; import { GameBar } from "./modules/game-bar/game-bar";
import { Screenshot } from "./utils/screenshot"; import { ScreenshotManager } from "./utils/screenshot-manager";
import { NativeMkbHandler } from "./modules/mkb/native-mkb-handler";
import { GuideMenu } from "./modules/ui/guide-menu";
import { updateVideoPlayer } from "./modules/stream/stream-settings-utils";
import { BlockFeature, NativeMkbMode, TouchControllerMode, UiSection } from "./enums/pref-values";
import { HeaderSection } from "./modules/ui/header";
import { GameTile } from "./modules/ui/game-tile";
import { ProductDetailsPage } from "./modules/ui/product-details";
import { NavigationDialogManager } from "./modules/ui/dialog/navigation-dialog";
import { GlobalPref, StreamPref } from "./enums/pref-keys";
import { UserAgent } from "./utils/user-agent";
import { XboxApi } from "./utils/xbox-api";
import { StreamStatsCollector } from "./utils/stream-stats-collector";
import { StreamSettings } from "./utils/stream-settings";
import { KeyboardShortcutHandler } from "./modules/mkb/keyboard-shortcut-handler";
import { GhPagesUtils } from "./utils/gh-pages";
import { DeviceVibrationManager } from "./modules/device-vibration-manager";
import { BxEventBus } from "./utils/bx-event-bus";
import { getGlobalPref, getStreamPref } from "./utils/pref-utils";
import { SettingsManager } from "./modules/settings-manager";
import { Toast } from "./utils/toast";
import { WebGPUPlayer } from "./modules/player/webgpu/webgpu-player";
import { StreamUiHandler } from "./modules/stream/stream-ui";
import { TrueAchievements } from "./utils/true-achievements";
SettingsManager.getInstance();
// Handle login page // Handle login page
if (window.location.pathname.includes('/auth/msa')) { if (window.location.pathname.includes('/auth/msa')) {
window.addEventListener('load', e => { const nativePushState = window.history['pushState'];
window.location.search.includes('loggedIn') && window.setTimeout(() => { window.history['pushState'] = function(...args: any[]) {
const location = window.location; const url = args[2];
// @ts-ignore if (url && (url.startsWith('/play') || url.substring(6).startsWith('/play'))) {
location.pathname.includes('/play') && location.reload(true); console.log('Redirecting to xbox.com/play');
}, 2000); window.stop();
}); window.location.href = 'https://www.xbox.com' + url;
return;
}
// @ts-ignore
return nativePushState.apply(this, arguments);
}
// Stop processing the script // Stop processing the script
throw new Error('[Better xCloud] Refreshing the page after logging in'); throw new Error('[Better xCloud] Refreshing the page after logging in');
} }
BxLogger.info('readyState', document.readyState); BxLogger.info('readyState', document.readyState);
if (BX_FLAGS.SafariWorkaround && document.readyState !== 'loading') { if (isFullVersion() && BX_FLAGS.SafariWorkaround && document.readyState !== 'loading') {
// Stop loading // Stop loading
window.stop(); window.stop();
// Show the reloading overlay // We need to set it to an empty string first to work around Bun's bug
const css = ` // https://github.com/oven-sh/bun/issues/12067
let css = '';
css += compressCss(`
.bx-reload-overlay { .bx-reload-overlay {
position: fixed; position: fixed;
top: 0; top: 0;
bottom: 0;
left: 0;
right: 0;
display: flex;
align-items: center;
background: #000000cc; background: #000000cc;
z-index: 9999; z-index: 9999;
width: 100%;
line-height: 100vh;
color: #fff; color: #fff;
text-align: center; text-align: center;
font-weight: 400; font-weight: 400;
font-family: "Segoe UI", Arial, Helvetica, sans-serif; font-family: "Segoe UI", Arial, Helvetica, sans-serif;
font-size: 1.3rem; font-size: 1.3rem;
} }
`;
.bx-reload-overlay *:focus {
outline: none !important;
}
.bx-reload-overlay > div {
margin: 0 auto;
}
.bx-reload-overlay a {
text-decoration: none;
display: inline-block;
background: #107c10;
color: white;
border-radius: 4px;
padding: 6px;
}
`);
const isSafari = UserAgent.isSafari();
let $secondaryAction: HTMLElement;
if (isSafari) {
$secondaryAction = CE('p', false, t('settings-reloading'));
} else {
$secondaryAction = CE('a', {
href: 'https://better-xcloud.github.io/troubleshooting',
target: '_blank',
}, '🤓 ' + t('how-to-fix'));
}
// Show the reloading overlay
const $fragment = document.createDocumentFragment(); const $fragment = document.createDocumentFragment();
$fragment.appendChild(CE('style', {}, css)); $fragment.appendChild(CE('style', false, css));
$fragment.appendChild(CE('div', {'class': 'bx-reload-overlay'}, t('safari-failed-message'))); $fragment.appendChild(CE('div',{
class: 'bx-reload-overlay',
},
CE('div', false,
CE('p', false, t('load-failed-message')),
$secondaryAction,
),
));
document.documentElement.appendChild($fragment); document.documentElement.appendChild($fragment);
// Reload the page // Reload the page if using Safari
// @ts-ignore // @ts-ignore
window.location.reload(true); isSafari && window.location.reload(true);
// Stop processing the script // Stop processing the script
throw new Error('[Better xCloud] Executing workaround for Safari'); throw new Error('[Better xCloud] Executing workaround for Safari');
} }
// Automatically reload the page when running into the "We are sorry..." error message
window.addEventListener('load', e => { window.addEventListener('load', e => {
// Automatically reload the page when running into the "We are sorry..." error message
window.setTimeout(() => { window.setTimeout(() => {
if (document.body.classList.contains('legacyBackground')) { if (document.body.classList.contains('legacyBackground')) {
// Has error message -> reload page // Has error message -> reload page
@@ -92,6 +159,33 @@ window.addEventListener('load', e => {
}, 3000); }, 3000);
}); });
document.addEventListener('readystatechange', e => {
if (document.readyState !== 'interactive') {
return;
}
STATES.isSignedIn = !!window.xbcUser?.isSignedIn;
if (STATES.isSignedIn) {
// Preload Remote Play
if (isFullVersion()) {
RemotePlayManager.getInstance()?.initialize();
}
} else {
// Show Settings button in the header when not signed in
// window.setTimeout(HeaderSection.watchHeader, 2000);
}
// Hide "Play with Friends" skeleton section
if (getGlobalPref(GlobalPref.UI_HIDE_SECTIONS).includes(UiSection.FRIENDS) || getGlobalPref(GlobalPref.BLOCK_FEATURES).includes(BlockFeature.FRIENDS)) {
const $parent = document.querySelector('div[class*=PlayWithFriendsSkeleton]')?.closest<HTMLElement>('div[class*=HomePage-module]');
$parent && ($parent.style.display = 'none');
}
// Preload fonts
preloadFonts();
})
window.BX_EXPOSED = BxExposed; window.BX_EXPOSED = BxExposed;
// Hide Settings UI when navigate to another page // Hide Settings UI when navigate to another page
@@ -103,140 +197,215 @@ window.addEventListener('popstate', onHistoryChanged);
window.history.pushState = patchHistoryMethod('pushState'); window.history.pushState = patchHistoryMethod('pushState');
window.history.replaceState = patchHistoryMethod('replaceState'); window.history.replaceState = patchHistoryMethod('replaceState');
window.addEventListener(BxEvent.XCLOUD_SERVERS_READY, e => { BxEventBus.Script.on('ui.header.rendered', () => {
// Start rendering UI HeaderSection.getInstance().checkHeader();
if (document.querySelector('div[class^=UnsupportedMarketPage]')) {
window.setTimeout(watchHeader, 2000);
} else {
watchHeader();
}
}); });
window.addEventListener(BxEvent.STREAM_LOADING, e => { BxEventBus.Stream.on('state.loading', () => {
// Get title ID for screenshot's name // Get title ID for screenshot's name
if (window.location.pathname.includes('/launch/')) { if (window.location.pathname.includes('/launch/') && STATES.currentStream.titleInfo) {
const matches = /\/launch\/(?<title_id>[^\/]+)\/(?<product_id>\w+)/.exec(window.location.pathname); STATES.currentStream.titleSlug = productTitleToSlug(STATES.currentStream.titleInfo.productInfo.title);
if (matches?.groups) {
STATES.currentStream.titleId = matches.groups.title_id;
STATES.currentStream.productId = matches.groups.product_id;
}
} else { } else {
STATES.currentStream.titleId = 'remote-play'; STATES.currentStream.titleSlug = 'remote-play';
STATES.currentStream.productId = '';
} }
// Setup UI
setupStreamUi();
}); });
// Setup loading screen // Setup loading screen
getPref(PrefKey.UI_LOADING_SCREEN_GAME_ART) && window.addEventListener(BxEvent.TITLE_INFO_READY, LoadingScreen.setup); getGlobalPref(GlobalPref.LOADING_SCREEN_GAME_ART) && BxEventBus.Script.on('titleInfo.ready', LoadingScreen.setup);
window.addEventListener(BxEvent.STREAM_STARTING, e => { BxEventBus.Stream.on('state.starting', () => {
// Hide loading screen // Hide loading screen
LoadingScreen.hide(); LoadingScreen.hide();
// Start hiding cursor if (isFullVersion()) {
if (!getPref(PrefKey.MKB_ENABLED) && getPref(PrefKey.MKB_HIDE_IDLE_CURSOR)) { // Start hiding cursor
MouseCursorHider.start(); const cursorHider = MouseCursorHider.getInstance();
MouseCursorHider.hide(); if (cursorHider) {
cursorHider.start();
cursorHider.hide();
}
} }
}); });
window.addEventListener(BxEvent.STREAM_PLAYING, e => { BxEventBus.Stream.on('state.playing', payload => {
const $video = (e as any).$video as HTMLVideoElement; if (isFullVersion()) {
STATES.currentStream.$video = $video; window.BX_STREAM_SETTINGS = StreamSettings.settings;
StreamSettings.refreshAllSettings();
}
STATES.isPlaying = true; STATES.isPlaying = true;
injectStreamMenuButtons();
if (getPref(PrefKey.GAME_BAR_POSITION) !== 'off') { if (isFullVersion()) {
const gameBar = GameBar.getInstance(); const gameBar = GameBar.getInstance();
gameBar.reset(); if (gameBar) {
gameBar.enable(); gameBar.reset();
gameBar.showBar(); gameBar.enable();
gameBar.showBar();
}
// Setup Keyboard shortcuts
KeyboardShortcutHandler.getInstance().start();
// Setup screenshot
const $video = payload.$video as HTMLVideoElement;
ScreenshotManager.getInstance().updateCanvasSize($video.videoWidth, $video.videoHeight);
// Setup local co-op
if (getStreamPref(StreamPref.LOCAL_CO_OP_ENABLED)) {
BxExposed.toggleLocalCoOp(true);
Toast.show(t('local-co-op'), t('enabled'));
}
} }
Screenshot.updateCanvasSize($video.videoWidth, $video.videoHeight); updateVideoPlayer();
updateVideoPlayerCss();
}); });
window.addEventListener(BxEvent.STREAM_ERROR_PAGE, e => { BxEventBus.Script.on('ui.error.rendered', () => {
BxEvent.dispatch(window, BxEvent.STREAM_STOPPED); BxEventBus.Stream.emit('state.stopped', {});
}); });
window.addEventListener(BxEvent.STREAM_STOPPED, e => { BxEventBus.Script.on('ui.guideHome.rendered', () => {
const $root = document.querySelector<HTMLElement>('#gamepass-dialog-root div[role=dialog] div[role=tabpanel] div[class*=HomeLandingPage]');
$root && GuideMenu.getInstance().injectHome($root, STATES.isPlaying);
});
BxEventBus.Script.on('ui.guideAchievementProgress.rendered', () => {
const $elm = document.querySelector('#gamepass-dialog-root button[class*=AchievementsButton-module__progressBarContainer]');
if ($elm) {
TrueAchievements.getInstance().injectAchievementsProgress($elm as HTMLElement);
}
});
BxEventBus.Script.on('ui.guideAchievementDetail.rendered', () => {
const $elm = document.querySelector('#gamepass-dialog-root div[class^=AchievementDetailPage-module]');
if ($elm) {
TrueAchievements.getInstance().injectAchievementDetailPage($elm as HTMLElement);
}
});
BxEventBus.Stream.on('ui.streamMenu.rendered', async () => {
await StreamUiHandler.handleStreamMenu();
});
BxEventBus.Stream.on('ui.streamHud.rendered', async () => {
const $elm = document.querySelector<HTMLElement>('#StreamHud');
$elm && StreamUiHandler.handleSystemMenu($elm);
});
isFullVersion() && window.addEventListener(BxEvent.XCLOUD_RENDERING_COMPONENT, e => {
const component = (e as any).component;
if (component === 'product-detail') {
ProductDetailsPage.injectButtons();
}
});
// Detect game change
BxEventBus.Stream.on('dataChannelCreated', payload => {
const { dataChannel } = payload;
if (dataChannel?.label !== 'message') {
return;
}
dataChannel.addEventListener('message', async (msg: MessageEvent) => {
if (msg.origin === 'better-xcloud' || typeof msg.data !== 'string') {
return;
}
if (!msg.data.includes('/titleinfo')) {
return;
}
// Get xboxTitleId from message
const currentStream = STATES.currentStream;
const json = JSON.parse(JSON.parse(msg.data).content);
const currentId = currentStream.xboxTitleId ?? null;
let newId: number = parseInt(json.titleid, 16);
// Get titleSlug for Remote Play
if (STATES.remotePlay.isPlaying) {
currentStream.titleSlug = 'remote-play';
if (json.focused) {
const productTitle = await XboxApi.getProductTitle(newId);
if (productTitle) {
currentStream.titleSlug = productTitleToSlug(productTitle);
} else {
newId = -1;
}
} else {
newId = 0;
}
}
if (currentId !== newId) {
currentStream.xboxTitleId = newId;
BxEventBus.Stream.emit('xboxTitleId.changed', {
id: newId,
});
}
});
});
function unload() {
if (!STATES.isPlaying) { if (!STATES.isPlaying) {
return; return;
} }
BxLogger.warning('Unloading');
if (isFullVersion()) {
KeyboardShortcutHandler.getInstance().stop();
// Stop MKB listeners
EmulatedMkbHandler.getInstance()?.destroy();
NativeMkbHandler.getInstance()?.destroy();
DeviceVibrationManager.getInstance()?.reset();
}
// Destroy StreamPlayer
STATES.currentStream.streamPlayerManager?.destroy();
STATES.isPlaying = false; STATES.isPlaying = false;
STATES.currentStream = {}; STATES.currentStream = {};
window.BX_EXPOSED.shouldShowSensorControls = false; window.BX_EXPOSED.shouldShowSensorControls = false;
window.BX_EXPOSED.stopTakRendering = false;
// Stop MKB listeners NavigationDialogManager.getInstance().hide();
getPref(PrefKey.MKB_ENABLED) && MkbHandler.INSTANCE.destroy(); StreamStats.getInstance().destroy();
StreamBadges.getInstance().destroy();
const $streamSettingsDialog = document.querySelector('.bx-stream-settings-dialog'); if (isFullVersion()) {
if ($streamSettingsDialog) { MouseCursorHider.getInstance()?.stop();
$streamSettingsDialog.classList.add('bx-gone'); TouchController.reset();
GameBar.getInstance()?.disable();
BxEventBus.Stream.emit('xboxTitleId.changed', { id: -1 });
} }
STATES.currentStream.audioGainNode = null;
STATES.currentStream.$video = null;
StreamStats.onStoppedPlaying();
MouseCursorHider.stop();
TouchController.reset();
GameBar.getInstance().disable();
});
window.addEventListener(BxEvent.CAPTURE_SCREENSHOT, e => {
Screenshot.takeScreenshot();
});
function observeRootDialog($root: HTMLElement) {
let currentShown = false;
const observer = new MutationObserver(mutationList => {
for (const mutation of mutationList) {
if (mutation.type !== 'childList') {
continue;
}
const shown = ($root.firstElementChild && $root.firstElementChild.childElementCount > 0) || false;
if (shown !== currentShown) {
currentShown = shown;
BxEvent.dispatch(window, shown ? BxEvent.XCLOUD_DIALOG_SHOWN : BxEvent.XCLOUD_DIALOG_DISMISSED);
}
}
});
observer.observe($root, {subtree: true, childList: true});
} }
function waitForRootDialog() { BxEventBus.Stream.on('state.stopped', unload);
const observer = new MutationObserver(mutationList => { window.addEventListener('pagehide', e => {
for (const mutation of mutationList) { BxEventBus.Stream.emit('state.stopped', {});
if (mutation.type !== 'childList') { });
continue;
}
const $target = mutation.target as HTMLElement; isFullVersion() && window.addEventListener(BxEvent.CAPTURE_SCREENSHOT, e => {
if ($target.id && $target.id === 'gamepass-dialog-root') { ScreenshotManager.getInstance().takeScreenshot();
observer.disconnect(); });
observeRootDialog($target);
break;
}
};
});
observer.observe(document.documentElement, {subtree: true, childList: true});
}
function main() { function main() {
waitForRootDialog(); GhPagesUtils.fetchLatestCommit();
if (isFullVersion()) {
if (getGlobalPref(GlobalPref.NATIVE_MKB_MODE) !== NativeMkbMode.OFF) {
const customList = getGlobalPref(GlobalPref.NATIVE_MKB_FORCED_GAMES);
BX_FLAGS.ForceNativeMkbTitles.push(...customList);
}
}
StreamSettings.setup();
// Monkey patches // Monkey patches
patchRtcPeerConnection(); patchRtcPeerConnection();
@@ -244,47 +413,61 @@ function main() {
interceptHttpRequests(); interceptHttpRequests();
patchVideoApi(); patchVideoApi();
patchCanvasContext(); patchCanvasContext();
isFullVersion() && AppInterface && patchPointerLockApi();
getPref(PrefKey.AUDIO_ENABLE_VOLUME_CONTROL) && patchAudioContext(); getGlobalPref(GlobalPref.AUDIO_VOLUME_CONTROL_ENABLED) && patchAudioContext();
getPref(PrefKey.BLOCK_TRACKING) && patchMeControl();
STATES.hasTouchSupport && TouchController.updateCustomList(); if (getGlobalPref(GlobalPref.BLOCK_TRACKING)) {
overridePreloadState(); patchMeControl();
disableAdobeAudienceManager();
VibrationManager.initialSetup(); }
// Check for Update
BX_FLAGS.CheckForUpdate && checkForUpdate();
// Setup UI // Setup UI
addCss(); addCss();
Toast.setup();
(getPref(PrefKey.GAME_BAR_POSITION) !== 'off') && GameBar.getInstance();
BX_FLAGS.PreloadUi && setupStreamUi();
StreamStatsCollector.setupEvents();
StreamBadges.setupEvents(); StreamBadges.setupEvents();
StreamStats.setupEvents(); StreamStats.setupEvents();
MkbHandler.setupEvents();
Patcher.init(); if (isFullVersion()) {
WebGPUPlayer.prepare();
disablePwa(); STATES.userAgent.capabilities.touch && TouchController.updateCustomList();
DeviceVibrationManager.getInstance();
// Check for Update
BX_FLAGS.CheckForUpdate && checkForUpdate();
Patcher.init();
disablePwa();
// Preload Remote Play
if (getGlobalPref(GlobalPref.REMOTE_PLAY_ENABLED)) {
RemotePlayManager.detect();
}
if (getGlobalPref(GlobalPref.TOUCH_CONTROLLER_MODE) === TouchControllerMode.ALL) {
TouchController.setup();
}
// Start PointerProviderServer
if (AppInterface && (getGlobalPref(GlobalPref.MKB_ENABLED) || getGlobalPref(GlobalPref.NATIVE_MKB_MODE) === NativeMkbMode.ON)) {
STATES.pointerServerPort = AppInterface.startPointerServer() || 9269;
BxLogger.info('startPointerServer', 'Port', STATES.pointerServerPort.toString());
}
// Show wait time in game card
getGlobalPref(GlobalPref.UI_GAME_CARD_SHOW_WAIT_TIME) && GameTile.setup();
EmulatedMkbHandler.setupEvents();
}
// Show a toast when connecting/disconecting controller // Show a toast when connecting/disconecting controller
window.addEventListener('gamepadconnected', e => showGamepadToast(e.gamepad)); if (getGlobalPref(GlobalPref.UI_CONTROLLER_SHOW_STATUS)) {
window.addEventListener('gamepaddisconnected', e => showGamepadToast(e.gamepad)); window.addEventListener('gamepadconnected', e => showGamepadToast(e.gamepad));
window.addEventListener('gamepaddisconnected', e => showGamepadToast(e.gamepad));
// Preload Remote Play
if (getPref(PrefKey.REMOTE_PLAY_ENABLED)) {
RemotePlay.detect();
} }
if (getPref(PrefKey.STREAM_TOUCH_CONTROLLER) === 'all') {
TouchController.setup();
}
// Start PointerProviderServer
(getPref(PrefKey.MKB_ENABLED)) && AppInterface && AppInterface.startPointerServer();
} }
main(); main();

41
src/macros/build.ts Normal file → Executable file
View File

@@ -1,12 +1,41 @@
import stylus from 'stylus'; import stylus from 'stylus';
import cssStr from "@assets/css/styles.styl" with { type: "text" }; export const isFullVersion = () => {
return Bun.env.BUILD_VARIANT === 'full';
};
const generatedCss = await (stylus(cssStr, {}) export const isLiteVersion = () => {
.set('filename', 'styles.css') return Bun.env.BUILD_VARIANT === 'lite';
.include('src/assets/css/')) };
.render();
export const renderStylus = async () => {
const file = Bun.file('./src/assets/css/styles.styl');
const cssStr = await file.text();
const generatedCss = await (stylus(cssStr, {})
.set('filename', 'styles.css')
.set('compress', true)
.include('src/assets/css/'))
.render();
export const renderStylus = () => {
return generatedCss; return generatedCss;
}; };
export const compressCss = (css: string) => {
return (stylus(css, {}).set('compress', true)).render();
};
export const compressCode = (code: string): string => {
return code.split('\n') // Split into lines
.map(line => line.startsWith('#') || line.startsWith('@') ? line + '\n' : line.trim()) // Trim spaces, with exceptions for shader files
.filter(line => line && !line.startsWith('//')) // Remove empty and commented lines
.join(''); // Join into a single line
};
export const compressCodeFile = async (path: string) => {
const file = Bun.file(path);
const code = await file.text();
return compressCode(code);
};

364
src/modules/controller-shortcut.ts Normal file → Executable file
View File

@@ -1,369 +1,55 @@
import { Screenshot } from "@utils/screenshot"; import { GamepadKey } from "@enums/gamepad";
import { GamepadKey } from "./mkb/definitions"; import { ShortcutHandler } from "@/utils/shortcut-handler";
import { PrompFont } from "@utils/prompt-font";
import { CE } from "@utils/html";
import { t } from "@utils/translation";
import { MkbHandler } from "./mkb/mkb-handler";
import { StreamStats } from "./stream/stream-stats";
import { MicrophoneShortcut } from "./shortcuts/shortcut-microphone";
import { StreamUiShortcut } from "./shortcuts/shortcut-stream-ui";
import { PrefKey, getPref } from "@utils/preferences";
import { SoundShortcut } from "./shortcuts/shortcut-sound";
import { BxEvent } from "@/utils/bx-event";
import { AppInterface } from "@/utils/global";
enum ShortcutAction {
STREAM_SCREENSHOT_CAPTURE = 'stream-screenshot-capture',
STREAM_MENU_SHOW = 'stream-menu-show',
STREAM_STATS_TOGGLE = 'stream-stats-toggle',
STREAM_SOUND_TOGGLE = 'stream-sound-toggle',
STREAM_MICROPHONE_TOGGLE = 'stream-microphone-toggle',
STREAM_VOLUME_INC = 'stream-volume-inc',
STREAM_VOLUME_DEC = 'stream-volume-dec',
DEVICE_SOUND_TOGGLE = 'device-sound-toggle',
DEVICE_VOLUME_INC = 'device-volume-inc',
DEVICE_VOLUME_DEC = 'device-volume-dec',
DEVICE_BRIGHTNESS_INC = 'device-brightness-inc',
DEVICE_BRIGHTNESS_DEC = 'device-brightness-dec',
}
export class ControllerShortcut { export class ControllerShortcut {
static readonly #STORAGE_KEY = 'better_xcloud_controller_shortcuts'; private static buttonsCache: { [key: string]: boolean[] } = {};
private static buttonsStatus: { [key: string]: boolean[] } = {};
static #buttonsCache: {[key: string]: boolean[]} = {};
static #buttonsStatus: {[key: string]: boolean[]} = {};
static #$selectProfile: HTMLSelectElement;
static #$selectActions: Partial<{[key in GamepadKey]: HTMLSelectElement}> = {};
static #$container: HTMLElement;
static #ACTIONS: {[key: string]: (ShortcutAction | null)[]} = {};
static reset(index: number) { static reset(index: number) {
ControllerShortcut.#buttonsCache[index] = []; ControllerShortcut.buttonsCache[index] = [];
ControllerShortcut.#buttonsStatus[index] = []; ControllerShortcut.buttonsStatus[index] = [];
} }
static handle(gamepad: Gamepad): boolean { static handle(gamepad: Gamepad): boolean {
const gamepadIndex = gamepad.index; const controllerSettings = window.BX_STREAM_SETTINGS.controllers[gamepad.id];
const actions = ControllerShortcut.#ACTIONS[gamepad.id]; if (!controllerSettings) {
return false;
}
const actions = controllerSettings.shortcuts;
if (!actions) { if (!actions) {
return false; return false;
} }
const gamepadIndex = gamepad.index;
// Move the buttons status from the previous frame to the cache // Move the buttons status from the previous frame to the cache
ControllerShortcut.#buttonsCache[gamepadIndex] = ControllerShortcut.#buttonsStatus[gamepadIndex].slice(0); ControllerShortcut.buttonsCache[gamepadIndex] = ControllerShortcut.buttonsStatus[gamepadIndex].slice(0);
// Clear the buttons status // Clear the buttons status
ControllerShortcut.#buttonsStatus[gamepadIndex] = []; ControllerShortcut.buttonsStatus[gamepadIndex] = [];
const pressed: boolean[] = []; const pressed: boolean[] = [];
let otherButtonPressed = false; let otherButtonPressed = false;
gamepad.buttons.forEach((button, index) => { const entries = gamepad.buttons.entries();
let index: GamepadKey;
let button: GamepadButton;
for ([index, button] of entries) {
// Only add the newly pressed button to the array (holding doesn't count) // Only add the newly pressed button to the array (holding doesn't count)
if (button.pressed && index !== GamepadKey.HOME) { if (button.pressed && index !== GamepadKey.HOME) {
otherButtonPressed = true; otherButtonPressed = true;
pressed[index] = true; pressed[index] = true;
// If this is newly pressed button -> run action // If this is newly pressed button -> run action
if (actions[index] && !ControllerShortcut.#buttonsCache[gamepadIndex][index]) { if (actions[index] && !ControllerShortcut.buttonsCache[gamepadIndex][index]) {
setTimeout(() => ControllerShortcut.#runAction(actions[index]!), 0); const idx = index;
setTimeout(() => ShortcutHandler.runAction(actions[idx]!), 0);
} }
} }
}); };
ControllerShortcut.#buttonsStatus[gamepadIndex] = pressed; ControllerShortcut.buttonsStatus[gamepadIndex] = pressed;
return otherButtonPressed; return otherButtonPressed;
} }
static #runAction(action: ShortcutAction) {
switch (action) {
case ShortcutAction.STREAM_SCREENSHOT_CAPTURE:
Screenshot.takeScreenshot();
break;
case ShortcutAction.STREAM_STATS_TOGGLE:
StreamStats.toggle();
break;
case ShortcutAction.STREAM_MICROPHONE_TOGGLE:
MicrophoneShortcut.toggle();
break;
case ShortcutAction.STREAM_MENU_SHOW:
StreamUiShortcut.showHideStreamMenu();
break;
case ShortcutAction.STREAM_SOUND_TOGGLE:
SoundShortcut.muteUnmute();
break;
case ShortcutAction.STREAM_VOLUME_INC:
SoundShortcut.adjustGainNodeVolume(10);
break;
case ShortcutAction.STREAM_VOLUME_DEC:
SoundShortcut.adjustGainNodeVolume(-10);
break;
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;
}
}
static #updateAction(profile: string, button: GamepadKey, action: ShortcutAction | null) {
if (!(profile in ControllerShortcut.#ACTIONS)) {
ControllerShortcut.#ACTIONS[profile] = [];
}
if (!action) {
action = null;
}
ControllerShortcut.#ACTIONS[profile][button] = action;
// Remove empty profiles
for (const key in ControllerShortcut.#ACTIONS) {
let empty = true;
for (const value of ControllerShortcut.#ACTIONS[key]) {
if (!!value) {
empty = false;
break;
}
}
if (empty) {
delete ControllerShortcut.#ACTIONS[key];
}
}
// Save to storage
window.localStorage.setItem(ControllerShortcut.#STORAGE_KEY, JSON.stringify(ControllerShortcut.#ACTIONS));
console.log(ControllerShortcut.#ACTIONS);
}
static #updateProfileList(e?: GamepadEvent) {
const $select = ControllerShortcut.#$selectProfile;
const $container = ControllerShortcut.#$container;
const $fragment = document.createDocumentFragment();
// Remove old profiles
while ($select.firstElementChild) {
$select.firstElementChild.remove();
}
const gamepads = navigator.getGamepads();
let hasGamepad = false;
for (const gamepad of gamepads) {
if (!gamepad || !gamepad.connected) {
continue;
}
// Ignore emulated gamepad
if (gamepad.id === MkbHandler.VIRTUAL_GAMEPAD_ID) {
continue;
}
hasGamepad = true;
const $option = CE<HTMLOptionElement>('option', {value: gamepad.id}, gamepad.id);
$fragment.appendChild($option);
}
if (hasGamepad) {
$select.appendChild($fragment);
$select.selectedIndex = 0;
$select.dispatchEvent(new Event('change'));
}
$container.dataset.hasGamepad = hasGamepad.toString();
}
static #switchProfile(profile: string) {
let actions = ControllerShortcut.#ACTIONS[profile];
if (!actions) {
actions = [];
}
// Reset selects' values
let button: any;
for (button in ControllerShortcut.#$selectActions) {
const $select = ControllerShortcut.#$selectActions[button as GamepadKey]!;
$select.value = actions[button] || '';
BxEvent.dispatch($select, 'change', {
ignoreOnChange: true,
});
}
}
static renderSettings() {
// Read actions from localStorage
ControllerShortcut.#ACTIONS = JSON.parse(window.localStorage.getItem(ControllerShortcut.#STORAGE_KEY) || '{}');
const buttons: Map<GamepadKey, PrompFont> = new Map();
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: {[key: string]: Partial<{[key in ShortcutAction]: string | string[]}>} = {
[t('device')]: AppInterface && {
[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')]: {
[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')],
}
};
const $baseSelect = CE<HTMLSelectElement>('select', {autocomplete: 'off'}, CE('option', {value: ''}, '---'));
for (const groupLabel in actions) {
const items = actions[groupLabel];
if (!items) {
continue;
}
const $optGroup = CE<HTMLOptGroupElement>('optgroup', {'label': groupLabel});
for (const action in items) {
let label = items[action as keyof typeof items];
if (!label) {
continue;
}
if (Array.isArray(label)) {
label = label.join(' ');
}
const $option = CE<HTMLOptionElement>('option', {value: action}, label);
$optGroup.appendChild($option);
}
$baseSelect.appendChild($optGroup);
}
let $remap: HTMLElement;
let $selectProfile: HTMLSelectElement;
const $container = CE('div', {'data-has-gamepad': 'false'},
CE('div', {},
CE('p', {'class': 'bx-shortcut-note'}, t('controller-shortcuts-connect-note')),
),
$remap = CE('div', {},
$selectProfile = CE('select', {'class': 'bx-shortcut-profile', autocomplete: 'off'}),
CE('p', {'class': 'bx-shortcut-note'},
CE('span', {'class': 'bx-prompt'}, PrompFont.HOME),
': ' + t('controller-shortcuts-xbox-note'),
),
),
);
$selectProfile.addEventListener('change', e => {
ControllerShortcut.#switchProfile($selectProfile.value);
});
const onActionChanged = (e: Event) => {
const $target = e.target as HTMLSelectElement;
const profile = $selectProfile.value;
const button: unknown = $target.dataset.button;
const action = $target.value as ShortcutAction;
const $fakeSelect = $target.previousElementSibling! as HTMLSelectElement;
let fakeText = '---';
if (action) {
const $selectedOption = $target.options[$target.selectedIndex];
const $optGroup = $selectedOption.parentElement as HTMLOptGroupElement;
fakeText = $optGroup.label + ' ' + $selectedOption.text;
}
($fakeSelect.firstElementChild as HTMLOptionElement).text = fakeText;
!(e as any).ignoreOnChange && ControllerShortcut.#updateAction(profile, button as GamepadKey, action);
};
// @ts-ignore
for (const [button, prompt] of buttons) {
const $row = CE('div', {'class': 'bx-shortcut-row'});
const $label = CE('label', {'class': 'bx-prompt'}, `${PrompFont.HOME} + ${prompt}`);
const $div = CE('div', {'class': 'bx-shortcut-actions'});
const $fakeSelect = CE<HTMLSelectElement>('select', {autocomplete: 'off'},
CE('option', {}, '---'),
);
$div.appendChild($fakeSelect);
const $select = $baseSelect.cloneNode(true) as HTMLSelectElement;
$select.dataset.button = button.toString();
$select.addEventListener('change', onActionChanged);
ControllerShortcut.#$selectActions[button] = $select;
$div.appendChild($select);
$row.appendChild($label);
$row.appendChild($div);
$remap.appendChild($row);
}
$container.appendChild($remap);
ControllerShortcut.#$selectProfile = $selectProfile;
ControllerShortcut.#$container = $container;
// Detect when gamepad connected/disconnect
window.addEventListener('gamepadconnected', ControllerShortcut.#updateProfileList);
window.addEventListener('gamepaddisconnected', ControllerShortcut.#updateProfileList);
ControllerShortcut.#updateProfileList();
return $container;
}
} }

View File

@@ -0,0 +1,143 @@
import { AppInterface, STATES } from "@utils/global";
import { StreamSettings } from "@/utils/stream-settings";
import { BxEventBus } from "@/utils/bx-event-bus";
const VIBRATION_DATA_MAP = {
gamepadIndex: 8,
leftMotorPercent: 8,
rightMotorPercent: 8,
leftTriggerMotorPercent: 8,
rightTriggerMotorPercent: 8,
durationMs: 16,
// delayMs: 16,
// repeat: 8,
};
type VibrationData = {
[key in keyof typeof VIBRATION_DATA_MAP]?: number;
}
export class DeviceVibrationManager {
private static instance: DeviceVibrationManager | null | undefined;
public static getInstance(): typeof DeviceVibrationManager['instance'] {
if (typeof DeviceVibrationManager.instance === 'undefined') {
if (STATES.browser.capabilities.deviceVibration) {
DeviceVibrationManager.instance = new DeviceVibrationManager();
} else {
DeviceVibrationManager.instance = null;
}
}
return DeviceVibrationManager.instance;
}
private dataChannel: RTCDataChannel | null = null;
private boundOnMessage: (e: MessageEvent) => void;
constructor() {
this.boundOnMessage = this.onMessage.bind(this);
BxEventBus.Stream.on('dataChannelCreated', payload => {
const { dataChannel } = payload;
if (dataChannel?.label === 'input') {
this.reset();
this.dataChannel = dataChannel;
this.setupDataChannel();
}
});
BxEventBus.Stream.on('deviceVibration.updated', () => this.setupDataChannel());
}
private setupDataChannel() {
if (!this.dataChannel) {
return;
}
this.removeEventListeners();
if (window.BX_STREAM_SETTINGS.deviceVibrationIntensity > 0) {
this.dataChannel.addEventListener('message', this.boundOnMessage);
}
}
private playVibration(data: Required<VibrationData>) {
const vibrationIntensity = StreamSettings.settings.deviceVibrationIntensity;
if (AppInterface) {
AppInterface.vibrate(JSON.stringify(data), vibrationIntensity);
return;
}
const realIntensity = Math.min(100, data.leftMotorPercent + data.rightMotorPercent / 2) * vibrationIntensity;
if (realIntensity === 0 || realIntensity === 100) {
// Stop vibration
window.navigator.vibrate(realIntensity ? data.durationMs : 0);
return;
}
const pulseDuration = 200;
const onDuration = Math.floor(pulseDuration * realIntensity / 100);
const offDuration = pulseDuration - onDuration;
const repeats = Math.ceil(data.durationMs / pulseDuration);
const pulses = Array(repeats).fill([onDuration, offDuration]).flat();
window.navigator.vibrate(pulses);
}
onMessage(e: MessageEvent) {
if (typeof e !== 'object' || !(e.data instanceof ArrayBuffer)) {
return;
}
const dataView = new DataView(e.data);
let offset = 0;
let messageType;
if (dataView.byteLength === 13) { // version >= 8
messageType = dataView.getUint16(offset, true);
offset += Uint16Array.BYTES_PER_ELEMENT;
} else {
messageType = dataView.getUint8(offset);
offset += Uint8Array.BYTES_PER_ELEMENT;
}
if (!(messageType & 128)) { // Vibration
return;
}
const vibrationType = dataView.getUint8(offset);
offset += Uint8Array.BYTES_PER_ELEMENT;
if (vibrationType !== 0) { // FourMotorRumble
return;
}
const data: VibrationData = {};
let key: keyof typeof VIBRATION_DATA_MAP;
for (key in VIBRATION_DATA_MAP) {
if (VIBRATION_DATA_MAP[key] === 16) {
data[key] = dataView.getUint16(offset, true);
offset += Uint16Array.BYTES_PER_ELEMENT;
} else {
data[key] = dataView.getUint8(offset);
offset += Uint8Array.BYTES_PER_ELEMENT;
}
}
this.playVibration(data as Required<VibrationData>);
}
private removeEventListeners() {
// Clear event listeners in previous DataChannel
try {
this.dataChannel?.removeEventListener('message', this.boundOnMessage);
} catch (e) {}
}
reset() {
this.removeEventListeners();
this.dataChannel = null;
}
}

View File

@@ -1,102 +0,0 @@
import { t } from "@utils/translation";
import { CE, createButton, ButtonStyle } from "@utils/html";
import { BxIcon } from "@utils/bx-icon";
type DialogOptions = Partial<{
title: string;
className: string;
content: string | HTMLElement;
hideCloseButton: boolean;
onClose: string;
helpUrl: string;
}>;
export class Dialog {
$dialog: HTMLElement;
$title: HTMLElement;
$content: HTMLElement;
$overlay: HTMLElement;
onClose: any;
constructor(options: DialogOptions) {
const {
title,
className,
content,
hideCloseButton,
onClose,
helpUrl,
} = options;
// Create dialog overlay
const $overlay = document.querySelector('.bx-dialog-overlay') as HTMLElement;
if (!$overlay) {
this.$overlay = CE('div', {'class': 'bx-dialog-overlay bx-gone'});
// Disable right click
this.$overlay.addEventListener('contextmenu', e => e.preventDefault());
document.documentElement.appendChild(this.$overlay);
} else {
this.$overlay = $overlay;
}
let $close;
this.onClose = onClose;
this.$dialog = CE('div', {'class': `bx-dialog ${className || ''} bx-gone`},
this.$title = CE('h2', {}, CE('b', {}, title),
helpUrl && createButton({
icon: BxIcon.QUESTION,
style: ButtonStyle.GHOST,
title: t('help'),
url: helpUrl,
}),
),
this.$content = CE('div', {'class': 'bx-dialog-content'}, content),
!hideCloseButton && ($close = CE('button', {type: 'button'}, t('close'))),
);
$close && $close.addEventListener('click', e => {
this.hide(e);
});
!title && this.$title.classList.add('bx-gone');
!content && this.$content.classList.add('bx-gone');
// Disable right click
this.$dialog.addEventListener('contextmenu', e => e.preventDefault());
document.documentElement.appendChild(this.$dialog);
}
show(newOptions: DialogOptions) {
// Clear focus
document.activeElement && (document.activeElement as HTMLElement).blur();
if (newOptions && newOptions.title) {
this.$title.querySelector('b')!.textContent = newOptions.title;
this.$title.classList.remove('bx-gone');
}
this.$dialog.classList.remove('bx-gone');
this.$overlay.classList.remove('bx-gone');
document.body.classList.add('bx-no-scroll');
}
hide(e?: any) {
this.$dialog.classList.add('bx-gone');
this.$overlay.classList.add('bx-gone');
document.body.classList.remove('bx-no-scroll');
this.onClose && this.onClose(e);
}
toggle() {
this.$dialog.classList.toggle('bx-gone');
this.$overlay.classList.toggle('bx-gone');
}
}

Some files were not shown because too many files have changed in this diff Show More