Compare commits

...

66 Commits

Author SHA1 Message Date
semantic-release-bot
34f6cf403b chore(release): 1.8.2 [CI SKIP] 2022-08-28 11:19:45 +00:00
Lukas Senionis
1dd48cd4c4 fix(utils): Mutable variable must be non-const (#19) 2022-08-28 07:19:06 -04:00
Lukas Senionis
6842f31190 Add necessary stuff to properly patch into play section (#18)
* feat(utils): Add singleShot option for patch functions

* chore(static-classes): add playSectionClasses

* feat(static-classes): Convert callbacks to one-liners
2022-08-28 07:14:19 -04:00
semantic-release-bot
27e1a9c8b0 chore(release): 1.8.1 [CI SKIP] 2022-08-26 17:35:29 +00:00
Lukas Senionis
55bd06a5ee fix(utils): update wrapReactClass impl. and defaults for prop argument (#17) 2022-08-26 13:34:56 -04:00
semantic-release-bot
58550b1f6e chore(release): 1.8.0 [CI SKIP] 2022-08-26 05:13:17 +00:00
AAGaming
c25fe58f08 feat(Focusable): add FooterLegend props to Focusable and Field 2022-08-26 01:12:21 -04:00
AAGaming
ef147c6715 chore(vscode): add tasks.json 2022-08-26 01:11:50 -04:00
AAGaming
43e9417303 chore(static-classes): add updaterFieldClasses 2022-08-26 01:11:32 -04:00
AAGaming
cf137c43b4 feat(components): add Carousel component 2022-08-26 01:11:04 -04:00
semantic-release-bot
8167be7642 chore(release): 1.7.8 [CI SKIP] 2022-08-20 15:09:34 +00:00
Lukas Senionis
a09af357c7 fix(Dropdown): correct Dropdown types (#15) 2022-08-20 11:08:50 -04:00
semantic-release-bot
e48180d7bb chore(release): 1.7.7 [CI SKIP] 2022-08-20 15:00:28 +00:00
Lukas Senionis
490a1f77fa fix(FieldProps): Add "bottomSeparator" option (#16) 2022-08-20 10:59:52 -04:00
semantic-release-bot
d1b47d21ed chore(release): 1.7.6 [CI SKIP] 2022-08-18 15:04:18 +00:00
Lukas Senionis
af98a76b86 fix(TextFieldProps): Add "disabled" option to TextFieldProps (#14) 2022-08-18 10:58:35 -04:00
semantic-release-bot
d18b1ba1ed chore(release): 1.7.5 [CI SKIP] 2022-08-18 00:26:12 +00:00
TrainDoctor
6be06446f2 fix(ButtonItem): update to account for both prop settings 2022-08-17 17:25:33 -07:00
semantic-release-bot
bb8a6df115 chore(release): 1.7.4 [CI SKIP] 2022-08-18 00:01:38 +00:00
AAGaming
a672230b00 fix(*): updates for webpack v5 2022-08-17 20:00:24 -04:00
semantic-release-bot
4644df8202 chore(release): 1.7.3 [CI SKIP] 2022-08-17 19:56:41 +00:00
Sefa Eyeoglu
7d3b5e8123 fix(Router): Add more members to Router interface (#12) 2022-08-17 15:55:50 -04:00
semantic-release-bot
0a4ef14239 chore(release): 1.7.2 [CI SKIP] 2022-08-17 19:19:46 +00:00
AAGaming
a592883a2e fix(utils): allow prop reassigns to fail 2022-08-17 15:18:45 -04:00
semantic-release-bot
3f126fe471 chore(release): 1.7.1 [CI SKIP] 2022-08-17 18:52:21 +00:00
AAGaming
e644de35d7 fix(utils): better method to wrap react classes 2022-08-17 14:51:24 -04:00
semantic-release-bot
9b79f70ac7 chore(release): 1.7.0 [CI SKIP] 2022-08-17 18:46:25 +00:00
AAGaming
d237bd48e4 feat(utils): add wrapReactClass 2022-08-17 14:45:34 -04:00
semantic-release-bot
f7d73b4dc3 chore(release): 1.6.2 [CI SKIP] 2022-08-15 17:13:15 +00:00
AAGaming
9edb1e6b97 chore(classes): add scrollClasses 2022-08-15 13:12:15 -04:00
AAGaming
605e4f5eae chore(semantic-release): tweak commit-analyzer 2022-08-15 13:11:38 -04:00
semantic-release-bot
e2f675835e chore(release): 1.6.1 [CI SKIP] 2022-08-13 01:36:15 +00:00
AAGaming
b7dc1d6275 fix(wrapReactType): try another method 2022-08-12 21:35:27 -04:00
semantic-release-bot
a1fdae9cd2 chore(release): 1.6.0 [CI SKIP] 2022-08-13 01:25:46 +00:00
AAGaming
7cf45cf371 feat(Utilities): add wrapReactType utility 2022-08-12 21:24:56 -04:00
semantic-release-bot
df1c8dbb8b chore(release): 1.5.1 [CI SKIP] 2022-08-10 22:46:24 +00:00
TrainDoctor
1de979f713 fix(security): update for minimist pollution exploit 2022-08-10 15:44:37 -07:00
TrainDoctor
f23070a82a Merge pull request #10 from SteamDeckHomebrew/dependabot/npm_and_yarn/semantic-release-19.0.3
chore(deps-dev): bump semantic-release from 19.0.2 to 19.0.3
2022-08-10 15:39:43 -07:00
dependabot[bot]
b87e7c2f40 chore(deps-dev): bump semantic-release from 19.0.2 to 19.0.3
Bumps [semantic-release](https://github.com/semantic-release/semantic-release) from 19.0.2 to 19.0.3.
- [Release notes](https://github.com/semantic-release/semantic-release/releases)
- [Commits](https://github.com/semantic-release/semantic-release/compare/v19.0.2...v19.0.3)

---
updated-dependencies:
- dependency-name: semantic-release
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-10 22:39:05 +00:00
semantic-release-bot
6db29a9a6e chore(release): 1.5.0 [CI SKIP] 2022-08-10 01:37:31 +00:00
AAGaming
e2126afd06 feat(ServerAPI): add Toaster to serverAPI 2022-08-09 21:36:32 -04:00
semantic-release-bot
cea587958e chore(release): 1.4.0 [CI SKIP] 2022-08-08 23:31:05 +00:00
AAGaming
b21dfcdb66 feat(utils): add findInTree and findInReactTree 2022-08-08 19:30:08 -04:00
semantic-release-bot
d46251bb91 chore(release): 1.3.0 [CI SKIP] 2022-08-02 22:55:55 +00:00
AAGaming
5b29447cfa feat(plugin): api for patching existing routes 2022-08-02 18:50:39 -04:00
TrainDoctor
e993d06963 Update README.md 2022-07-28 15:28:50 -07:00
semantic-release-bot
a3c136a07e chore(release): 1.2.4 [CI SKIP] 2022-07-25 19:44:53 +00:00
AAGaming
994b9e2cc6 fix(Modal): add closeModal 2022-07-25 15:44:06 -04:00
semantic-release-bot
801d70f300 chore(release): 1.2.3 [CI SKIP] 2022-07-25 19:34:45 +00:00
AAGaming
2fdfcdd478 fix(Modal): add another prop 2022-07-25 15:33:35 -04:00
semantic-release-bot
9a728611f4 chore(release): 1.2.2 [CI SKIP] 2022-07-25 19:30:30 +00:00
AAGaming
97997adfaf fix(Modal): add more props to typings 2022-07-25 15:29:38 -04:00
semantic-release-bot
279069ee34 chore(release): 1.2.1 [CI SKIP] 2022-07-13 01:07:46 +00:00
AAGaming
944a9024ff fix(ProgressBar): extend correct prop type 2022-07-12 21:06:46 -04:00
AAGaming
0a2220b4b4 chore(ProgressBar): formatting 2022-07-12 20:45:56 -04:00
semantic-release-bot
aa567e9dd0 chore(release): 1.2.0 [CI SKIP] 2022-07-13 00:34:28 +00:00
AAGaming
d9e4ff3ebd feat(ProgressBar): add new progress bars 2022-07-12 20:33:38 -04:00
semantic-release-bot
79d73dea2e chore(release): 1.1.0 [CI SKIP] 2022-07-09 14:34:48 +00:00
hulkrelax
602f93ae6a feat(Router.tsx): adds more methods to router (#9)
Adds the ability to get the main running app and to navigate to the running app
2022-07-09 10:34:09 -04:00
semantic-release-bot
d86c86cd80 chore(release): 1.0.2 [CI SKIP] 2022-07-07 04:03:42 +00:00
AAGaming
51c418d560 fix(Plugin): support non-ui plugins 2022-07-07 00:00:40 -04:00
semantic-release-bot
95de7346df chore(release): 1.0.1 [CI SKIP] 2022-06-29 15:53:25 +00:00
AAGaming
58933f827c fix(package): enable tree shaking 2022-06-29 11:50:20 -04:00
hulkrelax
0ff3476987 bugfix: Fix issue with incorrect typing on fetchNoCors (#7)
* make fetchNoCors have similar typing to fetch

* request param should be optional
2022-06-28 21:12:44 -04:00
semantic-release-bot
2c7d266c81 chore(release): 1.0.0 [CI SKIP] 2022-06-23 02:58:38 +00:00
AAGaming
9bd96227a0 refactor(components): rename Field components
BREAKING CHANGE: Toggle -> ToggleField Slider -> SliderField & add Toggle component
2022-06-22 22:57:51 -04:00
24 changed files with 922 additions and 814 deletions

View File

@@ -1,7 +1,15 @@
{
"branches": ["main", "dev"],
"plugins": [
"@semantic-release/commit-analyzer",
[
"@semantic-release/commit-analyzer",
{
"preset": "angular",
"releaseRules": [
{"type": "chore", "scope": "classes", "release": "patch"}
]
}
],
"@semantic-release/release-notes-generator",
"@semantic-release/changelog",
"@semantic-release/npm",

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

@@ -0,0 +1,15 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "dev",
"problemMatcher": [
"$tsc-watch"
],
"label": "npm: dev",
"detail": "tsc -b -w",
"isBackground": true
}
]
}

View File

@@ -1,3 +1,200 @@
## [1.8.2](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.8.1...v1.8.2) (2022-08-28)
### Bug Fixes
* **utils:** Mutable variable must be non-const ([#19](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/19)) ([1dd48cd](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/1dd48cd4c42989c75ee6859ebf8b857c027ff0f5))
## [1.8.1](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.8.0...v1.8.1) (2022-08-26)
### Bug Fixes
* **utils:** update wrapReactClass impl. and defaults for prop argument ([#17](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/17)) ([55bd06a](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/55bd06a5ee9468f572aed8f78be4d0acaaffe45a))
# [1.8.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.7.8...v1.8.0) (2022-08-26)
### Features
* **components:** add Carousel component ([cf137c4](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/cf137c43b4962977650e6ce0fad554b6ae966e43))
* **Focusable:** add FooterLegend props to Focusable and Field ([c25fe58](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/c25fe58f082d70a34443ac3c8b32b4528a4b01fb))
## [1.7.8](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.7.7...v1.7.8) (2022-08-20)
### Bug Fixes
* **Dropdown:** correct Dropdown types ([#15](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/15)) ([a09af35](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/a09af357c7e750377feefad86ab417b19484cb60))
## [1.7.7](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.7.6...v1.7.7) (2022-08-20)
### Bug Fixes
* **FieldProps:** Add "bottomSeparator" option ([#16](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/16)) ([490a1f7](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/490a1f77fa98a988f0cae61d74370bf3fa96336c))
## [1.7.6](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.7.5...v1.7.6) (2022-08-18)
### Bug Fixes
* **TextFieldProps:** Add "disabled" option to TextFieldProps ([#14](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/14)) ([af98a76](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/af98a76b86fb05942b9554adb369adbeaf27e70f))
## [1.7.5](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.7.4...v1.7.5) (2022-08-18)
### Bug Fixes
* **ButtonItem:** update to account for both prop settings ([6be0644](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/6be06446f2a7e0357b830be116fd25810c427dd5))
## [1.7.4](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.7.3...v1.7.4) (2022-08-18)
### Bug Fixes
* updates for webpack v5 ([a672230](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/a672230b0051dd942988554ec663ea7bfcd61cfe))
## [1.7.3](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.7.2...v1.7.3) (2022-08-17)
### Bug Fixes
* **Router:** Add more members to Router interface ([#12](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/12)) ([7d3b5e8](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/7d3b5e8123f6eeead1e2e227985069e54fe52572))
## [1.7.2](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.7.1...v1.7.2) (2022-08-17)
### Bug Fixes
* **utils:** allow prop reassigns to fail ([a592883](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/a592883a2eba52ba18876989acf939d12fa61fd3))
## [1.7.1](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.7.0...v1.7.1) (2022-08-17)
### Bug Fixes
* **utils:** better method to wrap react classes ([e644de3](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/e644de35d70680d17d70e89cc9b929a5aae08b48))
# [1.7.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.6.2...v1.7.0) (2022-08-17)
### Features
* **utils:** add wrapReactClass ([d237bd4](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/d237bd48e4b9e6436d7daefdf70327875e9e940d))
## [1.6.2](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.6.1...v1.6.2) (2022-08-15)
## [1.6.1](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.6.0...v1.6.1) (2022-08-13)
### Bug Fixes
* **wrapReactType:** try another method ([b7dc1d6](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/b7dc1d6275ed28b1e37b6cb512b2c5d1600a8f63))
# [1.6.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.5.1...v1.6.0) (2022-08-13)
### Features
* **Utilities:** add wrapReactType utility ([7cf45cf](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/7cf45cf3718d6e5295115f28a5f4dd24c6ff14e3))
## [1.5.1](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.5.0...v1.5.1) (2022-08-10)
### Bug Fixes
* **security:** update for minimist pollution exploit ([1de979f](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/1de979f7135c8d5eea1faca3d480d662c5e41d3d))
# [1.5.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.4.0...v1.5.0) (2022-08-10)
### Features
* **ServerAPI:** add Toaster to serverAPI ([e2126af](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/e2126afd06f339a22dbbaea89b834157a5975b96))
# [1.4.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.3.0...v1.4.0) (2022-08-08)
### Features
* **utils:** add findInTree and findInReactTree ([b21dfcd](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/b21dfcdb661fd7ad43213756dadb6cfdf0ac1e94))
# [1.3.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.2.4...v1.3.0) (2022-08-02)
### Features
* **plugin:** api for patching existing routes ([5b29447](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/5b29447cfa597773a81aa233da9362346683505d))
## [1.2.4](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.2.3...v1.2.4) (2022-07-25)
### Bug Fixes
* **Modal:** add closeModal ([994b9e2](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/994b9e2cc6f41da3d813e6f339bd2fd30e4fa3ad))
## [1.2.3](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.2.2...v1.2.3) (2022-07-25)
### Bug Fixes
* **Modal:** add another prop ([2fdfcdd](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/2fdfcdd4788ea0d6483e92729c3102212f3ec0fb))
## [1.2.2](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.2.1...v1.2.2) (2022-07-25)
### Bug Fixes
* **Modal:** add more props to typings ([97997ad](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/97997adfaf1a68ef436279e6e48f34f5eaa9531c))
## [1.2.1](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.2.0...v1.2.1) (2022-07-13)
### Bug Fixes
* **ProgressBar:** extend correct prop type ([944a902](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/944a9024ff20f0b596869564d014d7dd73e74254))
# [1.2.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.1.0...v1.2.0) (2022-07-13)
### Features
* **ProgressBar:** add new progress bars ([d9e4ff3](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/d9e4ff3ebd22a31306f564e7f8ad82879c8fb699))
# [1.1.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.0.2...v1.1.0) (2022-07-09)
### Features
* **Router.tsx:** adds more methods to router ([#9](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/9)) ([602f93a](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/602f93ae6a5ceca5383b888cd4803638799558c5))
## [1.0.2](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.0.1...v1.0.2) (2022-07-07)
### Bug Fixes
* **Plugin:** support non-ui plugins ([51c418d](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/51c418d560247c917125cd5534a978256724e5f3))
## [1.0.1](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.0.0...v1.0.1) (2022-06-29)
### Bug Fixes
* **package:** enable tree shaking ([58933f8](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/58933f827ce2e2ae9b162da4e0061a7591c5759d))
# [1.0.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v0.12.3...v1.0.0) (2022-06-23)
### Code Refactoring
* **components:** rename Field components ([9bd9622](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/9bd96227a0bb295dcc29abca71e37983307f0505))
### BREAKING CHANGES
* **components:** Toggle -> ToggleField Slider -> SliderField & add Toggle component
## [0.12.3](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v0.12.2...v0.12.3) (2022-06-23)

View File

@@ -1 +1,27 @@
# Decky Frontend Library
Library used to develop plugins used for use with [decky-loader](https://github.com/SteamDeckHomebrew/decky-loader).
## Decky Loader Discord [![Chat](https://img.shields.io/badge/chat-on%20discord-7289da.svg)](https://discord.gg/ZU74G2NJzk)
Please contact the developers here for questions and support that cannot be addressed via a Github issue.
## Developers and Contributors
This library is focused on usage by developers to provide custom React components based on those found in the Steam Deck's React UI.
This method allows developers to add UI elements and code without clobbering the existing UI of the deck in order to do so.
This library can also theoretically be used to extend existing UI elements of the Steam Deck UI but this has not been tested extensively.
### Getting Started (Contributors)
1. Clone the repository to your preferred location
2. If you wish to add features such as new UI components, please create a feature branch to PR to the original repo.
3. Bug/hotfixes are acceptable on the main branch,
### Getting Started (Developers)
If you would like a feature added to decky-frontend-lib, please request it via a Github issue.
If you want to start making a plugin with decky-frontend-lib, please direct your attention to the [decky-plugin-template](https://github.com/SteamDeckHomebrew/decky-plugin-template) repository.
This library can be found on [npm](https://www.npmjs.com/package/decky-frontend-lib) and as such you can pull it without a local copy for your project as needed.

946
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +1,11 @@
{
"name": "decky-frontend-lib",
"version": "0.12.3",
"version": "1.8.2",
"description": "A library for building decky plugins",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"module": "dist/index.js",
"sideEffects": false,
"scripts": {
"build": "shx rm -rf dist && tsc -b",
"dev": "tsc -b -w",
@@ -53,7 +54,7 @@
"import-sort-style-module": "^6.0.0",
"jest": "^27.5.1",
"prettier-plugin-import-sort": "^0.0.7",
"semantic-release": "^19.0.2",
"semantic-release": "^19.0.3",
"shx": "^0.3.4",
"ts-jest": "^27.1.4",
"typescript": "^4.6.3"
@@ -71,5 +72,8 @@
"style": "module",
"parser": "typescript"
}
},
"dependencies": {
"minimist": "^1.2.6"
}
}

View File

@@ -9,5 +9,5 @@ export interface ButtonItemProps extends ItemProps {
}
export const ButtonItem = Object.values(CommonUIModule).find((mod: any) =>
mod?.render?.toString()?.includes('childrenContainerWidth:"min"'),
mod?.render?.toString()?.includes('"highlightOnFocus","childrenContainerWidth"') || mod?.render?.toString()?.includes('childrenContainerWidth:"min"'),
) as FC<ButtonItemProps>;

View File

@@ -0,0 +1,28 @@
import { HTMLAttributes, ReactNode, RefAttributes, VFC } from "react";
import { findModuleChild } from "../webpack";
export interface CarouselProps extends HTMLAttributes<HTMLDivElement> {
autoFocus?: boolean;
enableBumperPaging?: boolean;
fnDoesItemTakeFocus?: (...unknown: any[]) => boolean;
fnGetColumnWidth?: (...unknown: any[]) => number;
fnGetId?: (id: number) => number;
fnItemRenderer?: (id: number, ...unknown: any[]) => ReactNode;
fnUpdateArrows?: (...unknown: any[]) => any;
initialColumn?: number;
nHeight?: number;
nIndexLeftmost?: number;
nItemHeight?: number;
nItemMarginX?: number;
nNumItems?: number;
name?: string;
scrollToAlignment?: 'center';
}
export const Carousel = findModuleChild((m) => {
if (typeof m !== 'object') return undefined;
for (let prop in m) {
if (m[prop]?.render?.toString().includes("setFocusedColumn:"))
return m[prop];
}
}) as VFC<CarouselProps & RefAttributes<HTMLDivElement>>;

View File

@@ -4,14 +4,14 @@ import { CommonUIModule } from '../webpack';
import { ItemProps } from './Item';
export interface SingleDropdownOption {
data: number;
label: string;
data: any;
label: ReactNode;
options?: never;
}
export interface MultiDropdownOption {
label: string;
label: ReactNode;
options: DropdownOption[];
data?: never;
@@ -21,7 +21,7 @@ export type DropdownOption = SingleDropdownOption | MultiDropdownOption;
export interface DropdownProps {
rgOptions: DropdownOption[];
selectedOption: number | null;
selectedOption: any;
disabled?: boolean;
onMenuWillOpen?(showMenu: () => void): void;
onMenuOpened?(): void;

View File

@@ -1,8 +1,10 @@
import { FC, HTMLAttributes, ReactNode, RefAttributes } from 'react';
import { findModuleChild } from '../webpack';
import { FooterLegendProps } from './FooterLegend';
export interface FieldProps extends HTMLAttributes<HTMLDivElement> {
export interface FieldProps extends HTMLAttributes<HTMLDivElement>, FooterLegendProps {
label?: string | ReactNode;
bottomSeparator?: boolean;
description?: string | ReactNode;
disabled?: boolean;
icon?: ReactNode;

View File

@@ -1,7 +1,8 @@
import { HTMLAttributes, ReactNode, RefAttributes, VFC } from "react";
import { findModuleChild } from "../webpack";
import { FooterLegendProps } from "./FooterLegend";
export interface FocusableProps extends HTMLAttributes<HTMLDivElement> {
export interface FocusableProps extends HTMLAttributes<HTMLDivElement>, FooterLegendProps {
children: ReactNode;
"flow-children"?: string;
focusClassName?: string;

View File

@@ -0,0 +1,18 @@
export interface FooterLegendProps {
actionDescriptionMap?: unknown;
onOKActionDescription?: string;
onCancelActionDescription?: string;
onSecondaryActionDescription?: string;
onOptionsActionDescription?: string;
onMenuActionDescription?: string;
onButtonDown?: () => void;
onButtonUp?: () => void;
onOKButton?: () => void;
onCancelButton?: () => void;
onSecondaryButton?: () => void;
onOptionsButton?: () => void;
onGamepadDirection?: () => void;
onGamepadFocus?: () => void;
onGamepadBlur?: () => void;
onMenuButton?: () => void;
}

View File

@@ -16,7 +16,15 @@ export interface ModalRootProps {
onMiddleButton?(): void;
onCancel?(): void;
onOK?(): void;
onEscKeypress?(): void;
closeModal?(): void;
className?: string;
modalClassName?: string;
bAllowFullSize?: boolean;
bDestructiveWarning?: boolean;
bDisableBackgroundDismiss?: boolean;
bHideCloseIcon?: boolean;
bOKDisabled?: boolean;
}
export const ModalRoot = findModuleChild((m) => {

View File

@@ -1,18 +1,44 @@
import { VFC } from 'react';
import { VFC, ReactNode } from 'react';
import { findModuleChild } from '../webpack';
import { ItemProps } from './Item';
export interface ProgressBarItem extends ItemProps {
export interface ProgressBarItemProps extends ItemProps {
indeterminate?: boolean;
nTransitionSec?: number;
nProgress?: number;
focusable?: boolean;
}
export interface ProgressBarProps {
indeterminate?: boolean;
nTransitionSec?: number;
nProgress?: number;
focusable?: boolean;
}
export interface ProgressBarWithInfoProps extends ProgressBarItemProps {
sTimeRemaining?: ReactNode;
sOperationText?: ReactNode;
}
export const ProgressBar = findModuleChild((m) => {
if (typeof m !== 'object') return undefined;
for (let prop in m) {
if (m[prop]?.toString()?.includes('.ProgressBar,"standard"==')) return m[prop];
}
}) as VFC<ProgressBarProps>;
export const ProgressBarWithInfo = findModuleChild((m) => {
if (typeof m !== 'object') return undefined;
for (let prop in m) {
if (m[prop]?.toString()?.includes('.ProgressBarFieldStatus},')) return m[prop];
}
}) as VFC<ProgressBarWithInfoProps>;
export const ProgressBarItem = findModuleChild((m) => {
if (typeof m !== 'object') return undefined;
for (let prop in m) {
if (m[prop]?.toString()?.includes('"indeterminate","nTransitionSec"')) return m[prop];
}
}) as VFC<ProgressBarItem>;
}) as VFC<ProgressBarItemProps>;

View File

@@ -17,19 +17,77 @@ export enum QuickAccessTab {
Decky,
}
export enum DisplayStatus {
Invalid = 0,
Launching = 1,
Uninstalling = 2,
Installing = 3,
Running = 4,
Validating = 5,
Updating = 6,
Downloading = 7,
Synchronizing = 8,
ReadyToInstall = 9,
ReadyToPreload = 10,
ReadyToLaunch = 11,
RegionRestricted = 12,
PresaleOnly = 13,
InvalidPlatform = 14,
PreloadComplete = 16,
BorrowerLocked = 17,
UpdatePaused = 18,
UpdateQueued = 19,
UpdateRequired = 20,
UpdateDisabled = 21,
DownloadPaused = 22,
DownloadQueued = 23,
DownloadRequired = 24,
DownloadDisabled = 25,
LicensePending = 26,
LicenseExpired = 27,
AvailForFree = 28,
AvailToBorrow = 29,
AvailGuestPass = 30,
Purchase = 31,
Unavailable = 32,
NotLaunchable = 33,
CloudError = 34,
CloudOutOfDate = 35,
Terminating = 36,
}
export type AppOverview = {
appid: string
display_name: string
display_status: DisplayStatus
sort_as: string
}
export interface Router {
CloseSideMenus(): void;
OpenQuickAccessMenu(quickAccessTab?: QuickAccessTab): void;
GetQuickAccessTab(): QuickAccessTab;
Navigate(path: string): void;
NavigateBackOrOpenMenu(): void;
NavigateToAppProperties(): void
NavigateToBugForum(): void
NavigateToExternalWeb(url: string): void;
NavigateToStore(): void;
NavigateToHelp(): void
NavigateToInvites(): void
NavigateToRunningApp(replace?: boolean): void;
NavigateToStorage(): void
NavigateToStore(): void
NavigateToStoreApp(appId: number | string): void
NavigateToStoreFreeToPlay(): void
NavigateToStoreManual(): void
NavigateToStoreNewReleases(): void
NavigateToStoreOnSale(): void
ToggleSideMenu(sideMenu: SideMenu): void;
CloseSideMenus(): void;
OpenSideMenu(sideMenu: SideMenu): void;
OpenPowerMenu(unknown?: any): void;
get RunningApps(): any;
get RunningApps(): AppOverview[];
get MainRunningApp(): AppOverview | undefined;
}
export const Router = findModuleChild((m: Module) => {

View File

@@ -9,7 +9,7 @@ export interface NotchLabel {
value?: number;
}
export interface SliderProps extends ItemProps {
export interface SliderFieldProps extends ItemProps {
value: number;
min?: number;
max?: number;
@@ -27,6 +27,6 @@ export interface SliderProps extends ItemProps {
onChange?(value: number): void;
}
export const Slider = Object.values(CommonUIModule).find((mod: any) =>
export const SliderField = Object.values(CommonUIModule).find((mod: any) =>
mod?.toString()?.includes('SliderField,fallback'),
) as FC<SliderProps>;
) as FC<SliderFieldProps>;

View File

@@ -6,6 +6,7 @@ export interface TextFieldProps {
label?: ReactNode;
requiredLabel?: ReactNode;
description?: ReactNode;
disabled?: boolean;
bShowCopyAction?: boolean;
bShowClearAction?: boolean;
bAlwaysShowClearAction?: boolean;

View File

@@ -1,14 +1,14 @@
import { FC } from 'react';
import { CommonUIModule } from '../webpack';
import { ItemProps } from './Item';
export interface ToggleProps extends ItemProps {
checked: boolean;
export interface ToggleProps {
value: boolean;
disabled?: boolean;
onChange?(checked: boolean): void;
navRef?: any; // TODO figure out what this is
}
export const Toggle = Object.values(CommonUIModule).find((mod: any) =>
mod?.render?.toString()?.includes('ToggleField,fallback'),
mod?.render?.toString()?.includes('.ToggleOff)'),
) as FC<ToggleProps>;

View File

@@ -0,0 +1,14 @@
import { FC } from 'react';
import { CommonUIModule } from '../webpack';
import { ItemProps } from './Item';
export interface ToggleFieldProps extends ItemProps {
checked: boolean;
disabled?: boolean;
onChange?(checked: boolean): void;
}
export const ToggleField = Object.values(CommonUIModule).find((mod: any) =>
mod?.render?.toString()?.includes('ToggleField,fallback'),
) as FC<ToggleFieldProps>;

View File

@@ -1,17 +1,20 @@
export * from './Button';
export * from './ButtonItem';
export * from './Carousel';
export * from './Dropdown';
export * from './Field';
export * from './Focusable';
export * from './FooterLegend';
export * from './Menu';
export * from './Modal';
export * from './Panel';
export * from './ProgressBar';
export * from './Router';
export * from './SidebarNavigation';
export * from './Slider';
export * from './SliderField';
export * from './Spinner';
export * from './static-classes';
export * from './SteamSpinner';
export * from './TextField';
export * from './Toggle';
export * from './ToggleField';

View File

@@ -63,6 +63,14 @@ type StaticClasses = Record<
string
>;
type ScrollClasses = Record<
| 'ScrollBoth'
| 'ScrollPanel'
| 'ScrollX'
| 'ScrollY',
string
>;
type GamepadDialogClasses = Record<
| 'duration-app-launch'
| 'GamepadDialogContent'
@@ -157,32 +165,151 @@ type QuickAccessControlsClasses = Record<
string
>;
export const staticClasses: StaticClasses = findModule((mod) => {
if (typeof mod !== 'object') return false;
type UpdaterFieldClasses = Record<
| "duration-app-launch"
| "OOBEUpdateStatusContainer"
| "UpdateScreen"
| "UpdatePanel"
| "CurrentStatus"
| "TotalUpdateSize"
| "ProgressInfoContainer"
| "TimeRemaining"
| "BatteryLowWarning"
| "fadeInAnimation"
| "ProgressStatus"
| "UpdateStatusContainer"
| "UpdaterFieldStatusSuccess"
| "UpdaterFieldStatusApplying"
| "TextContainer"
| "ApplyingText"
| "UpdateBytesRemaining"
| "Label"
| "Numerator"
| "Separator"
| "Denominator"
| "PatchNotes"
| "PostedTime"
| "EventDetailTitle"
| "EventDetailsSubTitle"
| "EventDetailsBody"
| "InsufficientBatteryText"
| "UnsupportedHardwareWarning"
| "Title"
| "Text"
| "Body"
| "ItemFocusAnim-darkerGrey-nocolor"
| "ItemFocusAnim-darkerGrey"
| "ItemFocusAnim-darkGrey"
| "ItemFocusAnim-grey"
| "ItemFocusAnimBorder-darkGrey"
| "ItemFocusAnim-green"
| "focusAnimation"
| "hoverAnimation",
string
>;
if (mod.TransitionMenuDelay) {
return true;
}
type PlaySectionClasses = Record<
| "AchievementCountLabel"
| "AchievementProgressRow"
| "ActionSection"
| "AppButtonsContainer"
| "Arrow"
| "AvatarAndPersona"
| "BreakNarrow"
| "BreakShort"
| "BreakTall"
| "BreakUltraWide"
| "BreakWide"
| "ClickablePlayBarItem"
| "CloudStatusIcon"
| "CloudStatusLabel"
| "CloudStatusRow"
| "CloudSyncProblem"
| "CloudSynching"
| "ComingSoon"
| "Container"
| "DetailsProgressBar"
| "DetailsProgressContainer"
| "DetailsSection"
| "DetailsSectionExtra"
| "DetailsSectionStatus"
| "DotDotDot"
| "DownloadPaused"
| "DownloadProgressBar"
| "Downloading"
| "FavoriteButton"
| "Favorited"
| "GameInfoButton"
| "GameStat"
| "GameStatIcon"
| "GameStatIconForced"
| "GameStatRight"
| "GameStatsSection"
| "GamepadUIBreakNarrow"
| "GamepadUIBreakShort"
| "GamepadUIBreakWide"
| "Glassy"
| "HideWhenNarrow"
| "Icon"
| "Icons"
| "InPage"
| "InnerContainer"
| "InvalidPlatform"
| "ItemFocusAnim-darkGrey"
| "ItemFocusAnim-darkerGrey"
| "ItemFocusAnim-darkerGrey-nocolor"
| "ItemFocusAnim-green"
| "ItemFocusAnim-grey"
| "ItemFocusAnimBorder-darkGrey"
| "Label"
| "LastPlayed"
| "LastPlayedInfo"
| "MenuActive"
| "MenuButton"
| "MiniAchievements"
| "OfflineMode"
| "OnlyDownloadBar"
| "PermanentlyUnavailable"
| "PlayBar"
| "PlayBarCloudStatusContainer"
| "PlayBarDetailLabel"
| "PlayBarGameIcon"
| "PlayBarGameName"
| "PlayBarIconAndGame"
| "PlayBarLabel"
| "Playtime"
| "PlaytimeIcon"
| "PlaytimeIconForced"
| "PortraitBar"
| "Presale"
| "RecentlyUpdated"
| "RecentlyUpdatedIcon"
| "RecentlyUpdatedLink"
| "RecentlyUpdatedText"
| "RightBreakNarrow"
| "RightBreakUltraNarrow"
| "RightBreakUltraWide"
| "RightBreakWide"
| "RightControls"
| "Row"
| "SharedLibrary"
| "StatusAndStats"
| "StatusNameContainer"
| "StickyHeader"
| "StickyHeaderShadow"
| "SuperimposedGridItems"
| "SyncAnim"
| "Visible"
| "duration-app-launch"
| "favorited"
| "focusAnimation"
| "hoverAnimation",
string
>;
return false;
});
export const gamepadDialogClasses: GamepadDialogClasses = findModule((mod) => {
if (typeof mod !== 'object') return false;
if (mod.WithFirstRow) {
return true;
}
return false;
});
export const quickAccessControlsClasses: QuickAccessControlsClasses = findModule((mod) => {
if (typeof mod !== 'object') return false;
if (mod.PanelSectionRow) {
return true;
}
return false;
});
export const staticClasses: StaticClasses = findModule((mod) => typeof mod === 'object' && mod.TransitionMenuDelay);
export const scrollClasses: ScrollClasses = findModule((mod) => typeof mod === 'object' && mod.ScrollPanel && mod.ScrollY);
export const gamepadDialogClasses: GamepadDialogClasses = findModule((mod) => typeof mod === 'object' && mod.WithFirstRow);
export const quickAccessControlsClasses: QuickAccessControlsClasses = findModule((mod) => typeof mod === 'object' && mod.PanelSectionRow);
export const updaterFieldClasses: UpdaterFieldClasses = findModule((mod) => typeof mod === 'object' && mod.PatchNotes && mod.PostedTime);
export const playSectionClasses: PlaySectionClasses = findModule((mod) => typeof mod === 'object' && mod.MenuButton && mod.MenuActive);

View File

@@ -1,10 +1,10 @@
import type { ComponentType } from 'react';
import type { ComponentType, ReactNode } from 'react';
import { RouteProps } from 'react-router';
export interface Plugin {
title: JSX.Element;
icon: JSX.Element;
content: JSX.Element;
content?: JSX.Element;
onDismount?(): void;
}
@@ -20,16 +20,37 @@ interface ServerResponseError {
type ServerResponse<TRes> = ServerResponseSuccess<TRes> | ServerResponseError;
interface RouterHook {
type RoutePatch = (route: RouteProps) => RouteProps;
export interface RouterHook {
addRoute(path: string, component: ComponentType, props?: Omit<RouteProps, 'path' | 'children'>): void;
addPatch(path: string, patch: RoutePatch): RoutePatch;
removePatch(path: string, patch: RoutePatch): void;
removeRoute(path: string): void;
}
export interface ToastData {
title: ReactNode;
body: ReactNode;
onClick?: () => void;
logo?: ReactNode;
icon?: ReactNode;
className?: string;
contentClassName?: string;
duration?: number
critical?: boolean
}
export interface Toaster {
toast(toast: ToastData): void;
}
export interface ServerAPI {
routerHook: RouterHook;
toaster: Toaster;
callPluginMethod<TArgs = {}, TRes = {}>(methodName: string, args: TArgs): Promise<ServerResponse<TRes>>;
callServerMethod<TArgs = {}, TRes = {}>(methodName: string, args: TArgs): Promise<ServerResponse<TRes>>;
fetchNoCors<TRes = {}>(url: string, request: RequestInfo): Promise<ServerResponse<TRes>>;
fetchNoCors<TRes = {}>(url: RequestInfo, request?: RequestInit): Promise<ServerResponse<TRes>>;
executeInTab(tab: string, runAsync: boolean, code: string): Promise<unknown>;
injectCssIntoTab<TRes = string>(tab: string, style: string): Promise<ServerResponse<TRes>>;
removeCssFromTab(tab: string, cssId: string): Promise<unknown>;

View File

@@ -42,22 +42,18 @@ export function fakeRenderComponent(fun: Function): any {
return res;
}
export function beforePatch(obj: any, name: string, fnc: (args: any[]) => any): void {
export interface PatchOptions {
singleShot?: boolean
}
export function beforePatch(obj: any, name: string, fnc: (args: any[]) => any, options: PatchOptions = {}): void {
const orig = obj[name];
obj[name] = function (...args: any[]) {
fnc.call(this, args);
return orig.call(this, ...args);
}
Object.assign(obj[name], orig);
obj[name].toString = () => orig.toString();
obj[name].__deckyOrig = orig;
}
export function afterPatch(obj: any, name: string, fnc: (args: any[], ret: any) => any): void {
const orig = obj[name];
obj[name] = function (...args: any[]) {
let ret = orig.call(this, ...args);
ret = fnc.call(this, args, ret);
const ret = orig.call(this, ...args);
if (options.singleShot) {
unpatch(obj, name);
}
return ret;
}
Object.assign(obj[name], orig);
@@ -65,11 +61,29 @@ export function afterPatch(obj: any, name: string, fnc: (args: any[], ret: any)
obj[name].__deckyOrig = orig;
}
export function replacePatch(obj: any, name: string, fnc: (args: any[]) => any): void {
export function afterPatch(obj: any, name: string, fnc: (args: any[], ret: any) => any, options: PatchOptions = {}): void {
const orig = obj[name];
obj[name] = function (...args: any[]) {
const ret = fnc.call(this, args);
if (ret == 'CALL_ORIGINAL') return orig.call(this, ...args);
let ret = orig.call(this, ...args);
ret = fnc.call(this, args, ret);
if (options.singleShot) {
unpatch(obj, name);
}
return ret;
}
Object.assign(obj[name], orig);
obj[name].toString = () => orig.toString();
obj[name].__deckyOrig = orig;
}
export function replacePatch(obj: any, name: string, fnc: (args: any[]) => any, options: PatchOptions = {}): void {
const orig = obj[name];
obj[name] = function (...args: any[]) {
let ret = fnc.call(this, args);
if (ret == 'CALL_ORIGINAL') ret = orig.call(this, ...args);
if (options.singleShot) {
unpatch(obj, name);
}
return ret;
};
Object.assign(obj[name], orig);
@@ -79,7 +93,19 @@ export function replacePatch(obj: any, name: string, fnc: (args: any[]) => any):
// TODO allow one method to be patched and unpatched multiple times independently using IDs in a Map or something
export function unpatch(obj: any, name: any): void {
obj[name] = obj[name].__deckyOrig;
if (obj[name].__deckyOrig !== undefined) {
obj[name] = obj[name].__deckyOrig;
}
}
export function wrapReactType(node: any, prop: any = 'type') {
return node[prop] = {...node[prop]};
}
export function wrapReactClass(node: any, prop: any = 'type') {
const cls = node[prop];
const wrappedCls = class extends cls {};
return node[prop] = wrappedCls;
}
export function getReactInstance(o: HTMLElement | Element | Node) {
@@ -92,4 +118,33 @@ export function joinClassNames(...classes: string[]): string {
export function sleep(ms: number) {
return new Promise(res => setTimeout(res, ms));
}
}
// Based on https://github.com/GooseMod/GooseMod/blob/9ef146515a9e59ed4e25665ed365fd72fc0dcf23/src/util/react.js#L20
export interface findInTreeOpts {
walkable?: string[],
ignore?: string[]
}
export declare type findInTreeFilter = (element: any) => boolean
export const findInTree = (parent: any, filter: findInTreeFilter, opts: findInTreeOpts): any => {
const { walkable = null, ignore = [] } = opts ?? {};
if (!parent || typeof parent !== 'object') { // Parent is invalid to search through
return null;
}
if (filter(parent)) return parent; // Parent matches, just return
if (Array.isArray(parent)) { // Parent is an array, go through values
return parent.map((x) => findInTree(x, filter, opts)).find((x) => x);
}
// Parent is an object, go through values (or option to only use certain keys)
return (walkable || Object.keys(parent)).map((x) => !ignore.includes(x) && findInTree(parent[x], filter, opts)).find((x: any) => x);
};
export const findInReactTree = (node: any, filter: findInTreeFilter) => findInTree(node, filter, { // Specialised findInTree for React nodes
walkable: [ 'props', 'children', 'child', 'sibling' ]
});

View File

@@ -1,6 +1,7 @@
declare global {
interface Window {
webpackJsonp: any;
webpackChunksteamui: any;
}
}
@@ -9,14 +10,33 @@ export type Module = any;
type FilterFn = (module: any) => boolean;
type FindFn = (module: any) => any;
const wpRequire = window.webpackJsonp.push([
[],
{ get_require: (mod: any, _exports: any, wpRequire: any) => (mod.exports = wpRequire) },
[['get_require']],
]);
export let webpackCache: any = {};
let hasWebpack5 = false;
export const allModules: Module[] = Object.keys(wpRequire.c)
.map((x) => wpRequire.c[x].exports)
if (window.webpackJsonp && !window.webpackJsonp.deckyShimmed) {
// Webpack 4, currently on stable
const wpRequire = window.webpackJsonp.push([
[],
{ get_require: (mod: any, _exports: any, wpRequire: any) => (mod.exports = wpRequire) },
[['get_require']],
]);
delete wpRequire.m.get_require;
delete wpRequire.c.get_require;
webpackCache = wpRequire.c;
} else {
// Webpack 5, currently on beta
hasWebpack5 = true;
const id = Math.random();
let initReq: any;
window.webpackChunksteamui.push([[ id ], {}, (r: any) => { initReq = r }]);
for (let i of Object.keys(initReq.m)) {
webpackCache[i] = initReq(i)
}
}
export const allModules: Module[] = hasWebpack5 ? Object.values(webpackCache).filter((x) => x) : Object.keys(webpackCache)
.map((x) => webpackCache[x].exports)
.filter((x) => x);
export const findModule = (filter: FilterFn) => {