Compare commits

...

200 Commits

Author SHA1 Message Date
semantic-release-bot
ef6be8c6ec chore(release): 3.18.2 [CI SKIP] 2022-12-11 20:12:52 +00:00
Lukas Senionis
767dc2fcee fix(useQuickAccessVisible): remove invalid prop access (#66) 2022-12-11 15:12:22 -05:00
semantic-release-bot
52305987c5 chore(release): 3.18.1 [CI SKIP] 2022-12-11 20:02:11 +00:00
Lukas Senionis
6f14da152a fix(findSP): fallback to last active context (#53) 2022-12-11 15:01:41 -05:00
semantic-release-bot
bb291b211c chore(release): 3.18.0 [CI SKIP] 2022-12-11 15:10:33 +00:00
Jozen Blue Martinez
88f245d476 feat(DialogCheckbox): Add DialogCheckbox component (#58)
* feat(DialogCheckbox): Add DialogCheckbox component

* fix(DialogCheckbox): Better sibling match

* fix(DialogCheckbox): Extend FocusableProps

* fix(DialogCheckbox): Extend FooterLegendProps

i should have probably tested that

* feat(DialogCheckbox): add onClick() prop

* fix(DialogCheckbox): replace default export with named export
2022-12-11 16:10:01 +01:00
semantic-release-bot
5bc78df918 chore(release): 3.17.0 [CI SKIP] 2022-12-11 15:08:02 +00:00
Jozen Blue Martinez
c586afb97d feat(ControlsList): Add ControlsList component (#61)
* feat(ControlsList): Add ControlsList component

* fix(ControlsList): replace default export with named export
2022-12-11 16:07:31 +01:00
semantic-release-bot
d9150c2556 chore(release): 3.16.2 [CI SKIP] 2022-12-11 14:48:15 +00:00
Jonas Dellinger
cd0635e94f fix(Marquee): replace default export with named export 2022-12-11 15:47:39 +01:00
semantic-release-bot
443c7850d7 chore(release): 3.16.1 [CI SKIP] 2022-12-11 14:46:16 +00:00
Jozen Blue Martinez
d24136ecb6 fix(FooterLegend): change description types to ReactNode (#62) 2022-12-11 15:45:40 +01:00
semantic-release-bot
55507446cc chore(release): 3.16.0 [CI SKIP] 2022-12-11 14:45:21 +00:00
Jozen Blue Martinez
925ea8c3ce feat(Marquee): Add Marquee component (#63) 2022-12-11 15:44:50 +01:00
semantic-release-bot
14c5210931 chore(release): 3.15.0 [CI SKIP] 2022-12-11 14:18:49 +00:00
Jozen Blue Martinez
cc29ddaf57 feat(Focusable): add noFocusRing prop type (#65) 2022-12-11 15:18:07 +01:00
semantic-release-bot
1e8979b641 chore(release): 3.14.0 [CI SKIP] 2022-12-10 00:14:13 +00:00
jurassicplayer
7ba1229a4e feat(toast): add showToast/playSound to ToastData (#64) 2022-12-09 19:13:37 -05:00
semantic-release-bot
4c2a715324 chore(release): 3.13.0 [CI SKIP] 2022-11-29 20:01:20 +00:00
Jozen Blue Martinez
678ba216f1 feat(Menu): add more missing props (#60) [CI SKIP]
* feat(Menu): extend FooterLegendProps

* feat(MenuItem): add more missing props
2022-11-29 21:00:45 +01:00
semantic-release-bot
07d15f5dca chore(release): 3.12.0 [CI SKIP] 2022-11-28 12:03:43 +00:00
Jozen Blue Martinez
c84a091469 feat(MenuItem): add missing props (#59) 2022-11-28 07:03:07 -05:00
semantic-release-bot
47fd13692f chore(release): 3.11.1 [CI SKIP] 2022-11-20 01:13:29 +00:00
AAGaming
2ec9519b7d fix(Footer): add types for ActionDescriptionMap 2022-11-19 20:12:45 -05:00
semantic-release-bot
24606190e0 chore(release): 3.11.0 [CI SKIP] 2022-11-18 18:00:30 +00:00
Lukas Senionis
ed98d14b37 feat(classes): add "appDetailsClasses" (#55) 2022-11-18 12:59:57 -05:00
semantic-release-bot
b882612dfa chore(release): 3.10.0 [CI SKIP] 2022-11-18 17:37:50 +00:00
Lukas Senionis
32291620b4 feat(classes): add appDetailsHeaderClasses (#54) 2022-11-18 12:37:16 -05:00
semantic-release-bot
9b368c5f11 chore(release): 3.9.0 [CI SKIP] 2022-11-16 20:45:13 +00:00
Lukas Senionis
e167ef5a13 feat(Dialog): add "focusable" button prop (#51) 2022-11-16 15:44:36 -05:00
semantic-release-bot
2f3df00967 chore(release): 3.8.0 [CI SKIP] 2022-11-11 21:05:54 +00:00
AAGaming
215156d316 feat(routerhook): add global components support 2022-11-11 16:05:05 -05:00
semantic-release-bot
47d75db690 chore(release): 3.7.14 [CI SKIP] 2022-11-05 01:47:25 +00:00
AAGaming
82768e0415 fix(Menu): fix on Steam beta 2022-11-04 21:46:39 -04:00
AAGaming
e44187fe4b fix(Modal): fix on Steam beta 2022-11-04 21:46:22 -04:00
semantic-release-bot
72af32436e chore(release): 3.7.13 [CI SKIP] 2022-11-02 16:56:44 +00:00
Lukas Senionis
e1f64a38de fix(useQuickAccessVisible): make it work in beta (#49)
* fix(useQuickAccessVisible): make it work in beta

* fix(useQuickAccessVisible): rename for consistency

* fix(useQuickAccessVisible): update the exports

* fix(useQuickAccessVisible): shut up typescript
2022-11-02 12:56:11 -04:00
TrainDoctor
82ed48761d Merge pull request #38 from Tormak9970/main
Added some of the SteamClient types
2022-10-30 19:36:12 -07:00
TrainDoctor
a81c342d2a Update src/deck-components/index.ts 2022-10-30 17:35:48 -07:00
TrainDoctor
68d630262d Merge pull request #45 from FrogTheFrog/patch-19
Extend CI for PRs
2022-10-29 15:16:22 -07:00
semantic-release-bot
0bb8c67cfa chore(release): 3.7.12 [CI SKIP] 2022-10-29 19:44:41 +00:00
Jonas Dellinger
edd29e6c5a Merge pull request #46 from FrogTheFrog/patch-20
fix(Item): change title and description types to ReactNode
2022-10-29 21:44:13 +02:00
Lukas Senionis
0ed054fae9 fix(Item): change title and description types to ReactNode 2022-10-29 13:25:30 +03:00
semantic-release-bot
0d912eac88 chore(release): 3.7.11 [CI SKIP] 2022-10-28 23:04:53 +00:00
AAGaming
789e16380f fix(package.json): train wtf 2022-10-28 19:04:25 -04:00
semantic-release-bot
88b50bbc1e chore(release): 3.7.10 [CI SKIP] 2022-10-28 22:56:58 +00:00
AAGaming
75f35882f2 fix(tabs): shut up typescript 2022-10-28 18:56:30 -04:00
semantic-release-bot
23af4c0bb4 chore(release): 3.7.9 [CI SKIP] 2022-10-28 22:43:16 +00:00
AAGaming
a074277bb5 fix(tabs): fix on stable for real this time i think 2022-10-28 18:42:28 -04:00
Lukas Senionis
9c72a55aff Remove prepack hook 2022-10-28 22:00:33 +03:00
Lukas Senionis
92ffc76075 Extend CI for PRs 2022-10-27 11:48:44 +03:00
semantic-release-bot
fb49d64fd3 chore(release): 3.7.8 [CI SKIP] 2022-10-26 21:54:08 +00:00
Lukas Senionis
cfef1dc320 fix(Field): fix this time for real (#44) 2022-10-26 17:53:41 -04:00
semantic-release-bot
f6b4d6b254 chore(release): 3.7.7 [CI SKIP] 2022-10-26 21:49:50 +00:00
Lukas Senionis
0010a1fcee fix(Field): remove incompatible properties (#42)
* fix(Field): remove incompatible properties

* fix(Field): remove the override
2022-10-26 17:49:14 -04:00
semantic-release-bot
28cbc1cfe1 chore(release): 3.7.6 [CI SKIP] 2022-10-26 21:42:37 +00:00
Lukas Senionis
fe75dfb5f4 fix(Field): add override for onClick type (#43) 2022-10-26 17:42:04 -04:00
semantic-release-bot
91c386a6cc chore(release): 3.7.5 [CI SKIP] 2022-10-26 12:19:45 +00:00
Lukas Senionis
bedb6b8bb9 fix(Field): add types for focusing field (#41) 2022-10-26 08:19:16 -04:00
semantic-release-bot
4cdcca0b5a chore(release): 3.7.4 [CI SKIP] 2022-10-26 00:16:38 +00:00
AAGaming
f16e0b29f8 fix(tabs): fix on stable 2022-10-25 20:16:06 -04:00
AAGaming
37a6658b95 chore(docs): how did i manage to do this 2022-10-24 20:45:01 -04:00
AAGaming
ed0b92de2e fix(docs): change arg format 2022-10-24 20:43:53 -04:00
AAGaming
dcba5c22f8 chore(docs): fix it 2022-10-24 20:42:47 -04:00
semantic-release-bot
fa50ca6a37 chore(release): 3.7.3 [CI SKIP] 2022-10-25 00:38:49 +00:00
AAGaming
19e986ed8b chore(ci): switch to npm 2022-10-24 20:38:18 -04:00
AAGaming
3c553a227d fix(tabs): it returns 2022-10-24 20:35:01 -04:00
AAGaming
1f2694aec8 chore(prettier): fix prettier 2022-10-24 20:33:40 -04:00
semantic-release-bot
2e52cca8a2 chore(release): 3.7.2 [CI SKIP] 2022-10-24 14:59:05 +00:00
AAGaming
3dbca1a056 fix(tabs): unkill build 2022-10-24 10:58:35 -04:00
semantic-release-bot
c6692138c6 chore(release): 3.7.1 [CI SKIP] 2022-10-24 05:01:13 +00:00
AAGaming
25c33b2a05 fix(Tabs): temp remove until we have a way to grab it on beta 2022-10-24 01:00:44 -04:00
semantic-release-bot
00d27d1373 chore(release): 3.7.0 [CI SKIP] 2022-10-24 00:22:31 +00:00
AAGaming
5f0470c351 feat(modal): support for latest steamos preview 2022-10-23 20:22:04 -04:00
semantic-release-bot
c77d6edaae chore(release): 3.6.1 [CI SKIP] 2022-10-19 19:57:14 +00:00
Lukas Senionis
c44c66facd fix(plugin): export RoutePatch (#39) 2022-10-19 15:44:13 -04:00
Tormak
ce3860f73b added jsdoc and SteamClient global declaration 2022-10-17 08:39:37 -04:00
Tormak
d8b10a2133 ran prettier 2022-10-15 22:46:41 -05:00
Tormak
1581304dcb restyled 2022-10-15 22:44:58 -05:00
Travis Lane
60ddf474e0 Merge branch 'SteamDeckHomebrew:main' into main 2022-10-15 13:46:50 -05:00
Tormak
52ae328e2e added some of the SteamClient types 2022-10-15 14:35:57 -05:00
semantic-release-bot
276e4eccd2 chore(release): 3.6.0 [CI SKIP] 2022-10-15 03:44:35 +00:00
AAGaming
2fc2060a6c feat(plugin): add alwaysRender 2022-10-14 23:43:45 -04:00
semantic-release-bot
1143a9f3e0 chore(release): 3.5.6 [CI SKIP] 2022-10-08 12:57:07 +00:00
Lukas Senionis
5a5218a7c4 fix(Dialog): remove not exported dialog button (#37) 2022-10-08 08:56:14 -04:00
semantic-release-bot
8a887ca858 chore(release): 3.5.5 [CI SKIP] 2022-10-08 05:54:20 +00:00
AAGaming
0ce1b5499d fix(sidebarnavigation): no dont 2022-10-08 01:53:35 -04:00
semantic-release-bot
554163cc5d chore(release): 3.5.4 [CI SKIP] 2022-10-08 05:51:16 +00:00
AAGaming
d6b00b0733 fix(sidebarnavigation): allow null pags 2022-10-08 01:50:25 -04:00
semantic-release-bot
f8ddf210f0 chore(release): 3.5.3 [CI SKIP] 2022-10-08 02:52:44 +00:00
AAGaming
4024b76918 fix(tabs): fix props and add example 2022-10-07 22:51:59 -04:00
semantic-release-bot
245dd0f3cf chore(release): 3.5.2 [CI SKIP] 2022-10-08 02:22:06 +00:00
AAGaming
7161e757e9 fix(Tabs): make onShowTab required 2022-10-07 22:21:30 -04:00
semantic-release-bot
c60d1e9787 chore(release): 3.5.1 [CI SKIP] 2022-10-08 02:11:59 +00:00
AAGaming
0e0e0d204a fix(Tabs): actually export it lmao 2022-10-07 22:11:23 -04:00
semantic-release-bot
e5120928d3 chore(release): 3.5.0 [CI SKIP] 2022-10-08 02:04:42 +00:00
AAGaming
abbd3cddae feat(Tabs): initial tabs component, props, docs 2022-10-07 22:04:02 -04:00
semantic-release-bot
621e47c6a0 chore(release): 3.4.0 [CI SKIP] 2022-10-06 01:43:25 +00:00
OMGDuke
e2920dd91e feat(hooks): Added useParams hook (#36) 2022-10-05 21:42:54 -04:00
semantic-release-bot
67a76e2691 chore(release): 3.3.5 [CI SKIP] 2022-10-02 16:38:31 +00:00
AAGaming
0f205e8916 fix(docs): set categorizeByGroup to true 2022-10-02 12:37:45 -04:00
semantic-release-bot
472307e4a4 chore(release): 3.3.4 [CI SKIP] 2022-10-02 16:34:00 +00:00
AAGaming
fbd936dc1f fix(docs): build each component as a seperate page 2022-10-02 12:33:20 -04:00
AAGaming
33dd4e5548 chore(docs): initial typedoc setup (#35) 2022-10-02 12:22:58 -04:00
semantic-release-bot
4b76ccd91a chore(release): 3.3.3 [CI SKIP] 2022-10-02 12:36:26 +00:00
Lukas Senionis
99ad7543a9 fix(modal): make children optional (#34) 2022-10-02 08:36:00 -04:00
semantic-release-bot
b5dc08a977 chore(release): 3.3.2 [CI SKIP] 2022-10-02 02:31:09 +00:00
AAGaming
40871af853 fix(modal): allow children 2022-10-01 22:30:33 -04:00
Lukas Senionis
c910dbde79 Add dialog components (#22) 2022-10-01 21:41:47 -04:00
Lukas Senionis
4cbb30c21c Add custom-hook: useQuickAccessVisible (#28) 2022-10-01 21:38:46 -04:00
semantic-release-bot
54a1ef6201 chore(release): 3.3.1 [CI SKIP] 2022-10-02 01:33:33 +00:00
Barend Du Toit
ed0be5e87e fix(SidebarNavigation): add more props (#29) 2022-10-01 21:33:02 -04:00
Travis Lane
a064163b49 updated static-classes (#26) 2022-10-01 21:32:23 -04:00
semantic-release-bot
0b4fcb8d49 chore(release): 3.3.0 [CI SKIP] 2022-10-02 01:31:47 +00:00
Barend Du Toit
4233128c7e feat(Menu): add nested menu groups + more props (#30) 2022-10-01 21:31:18 -04:00
TrainDoctor
43cb2726d8 chore(license): remove un-needed exceptions 2022-09-30 21:50:23 -07:00
TrainDoctor
5d9c506fe7 Update package.json 2022-09-30 21:48:33 -07:00
Tormak
aa0fad2ecf fixed typo 2022-09-29 13:24:15 -05:00
Tormak
dab9071d1e found name of static classes 2022-09-29 13:01:06 -05:00
semantic-release-bot
35a061759a chore(release): 3.2.2 [CI SKIP] 2022-09-29 16:36:04 +00:00
Lukas Senionis
1fbe55aa54 fix(modal): extend props for modals (#32) 2022-09-29 12:34:00 -04:00
Tormak
189a90ba31 Updated ScrollPanelClasses
marked the old variable as depreciated and moved the implementation to the new variable
2022-09-25 09:06:14 -05:00
semantic-release-bot
66eb0cbbf3 chore(release): 3.2.1 [CI SKIP] 2022-09-24 17:49:57 +00:00
Lukas Senionis
6996e5424f fix(modal): update showModal types (#27) 2022-09-24 13:49:23 -04:00
Tormak
8509ae8f9a Merge branch 'main' of https://github.com/Tormak9970/decky-frontend-lib 2022-09-24 12:05:06 -05:00
Tormak
a6ebfdcd7d fixed includes 2022-09-24 12:05:04 -05:00
Tormak
5f7655baaf fixed bugs with static-classes and future proofing 2022-09-24 11:51:19 -05:00
Travis Lane
bca2dcc9bd Merge branch 'SteamDeckHomebrew:main' into main 2022-09-24 10:54:09 -05:00
Tormak
546a4da043 updated DialogButton props and added nav pref enum 2022-09-22 17:33:08 -05:00
Travis Lane
ad643836f0 updated DialogButton props and added nav pref enum (#25) 2022-09-22 17:50:07 -04:00
semantic-release-bot
b39ba26b28 chore(release): 3.2.0 [CI SKIP] 2022-09-20 21:37:13 +00:00
AAGaming
130dfa24c5 feat(FooterLegend): add GamepadEvent 2022-09-20 17:36:16 -04:00
Martmists
71babc82c8 Add ColorPickerModal to exports (#24) 2022-09-20 08:46:37 -04:00
semantic-release-bot
bb440deed6 chore(release): 3.1.4 [CI SKIP] 2022-09-19 21:33:33 +00:00
TrainDoctor
064c161b67 fix(License): update license in package.json 2022-09-19 14:32:37 -07:00
TrainDoctor
6abf0efc10 Update package.json 2022-09-19 14:30:08 -07:00
TrainDoctor
537b8301ec Add license exception for static linking 2022-09-18 14:31:18 -07:00
Beebles
2ae87da37a Add ColorPickerModal component (with issues fixed) to custom-components (#21) 2022-09-18 17:03:00 -04:00
semantic-release-bot
0d248f3987 chore(release): 3.1.3 [CI SKIP] 2022-09-18 19:38:35 +00:00
AAGaming
74aefc7b3c fix(DialogButton): fix ref type 2022-09-18 15:37:46 -04:00
semantic-release-bot
16ec1e3d2e chore(release): 3.1.2 [CI SKIP] 2022-09-18 19:18:58 +00:00
AAGaming
db41e74cf9 fix(DialogButton): fix types, add ref types 2022-09-18 15:17:56 -04:00
semantic-release-bot
6441bde835 chore(release): 3.1.1 [CI SKIP] 2022-09-18 19:02:37 +00:00
AAGaming
29201fadaf fix(Button): add types to DialogButton
also remove useless Button component.
2022-09-18 15:01:44 -04:00
semantic-release-bot
31b81f5edd chore(release): 3.1.0 [CI SKIP] 2022-09-18 18:52:48 +00:00
AAGaming
b05f84658a feat(components): add FocusRing 2022-09-18 14:51:50 -04:00
Lukas Senionis
218e007234 Fix Field and Item types (#23)
* fix(field): correct and extend FieldProps

* fix(item): correct bottomSeparator in ItemProps

* fix(field): correct field commentary

* feat(field): add comment about inlineWrap
2022-09-17 09:57:36 -04:00
TrainDoctor
aa35e8b64a Create LICENSE 2022-09-15 20:44:15 -07:00
semantic-release-bot
cd61f57a6f chore(release): 3.0.0 [CI SKIP] 2022-09-09 20:19:05 +00:00
AAGaming
8eb921e8b7 feat(serverAPI): add FilePicker 2022-09-09 16:18:06 -04:00
AAGaming
26017e7de4 feat(modal): add more props, refactor
BREAKING CHANGE: ModalRoot ->ConfirmModal
add the actual ModalRoot which does not contain buttons
2022-09-09 16:17:44 -04:00
AAGaming
71c7afa1a6 fix(textfield): extend HTMLAttributes 2022-09-09 16:16:15 -04:00
AAGaming
d6a08feca0 fix(button): add style prop 2022-09-09 16:15:51 -04:00
semantic-release-bot
160fbb493f chore(release): 2.0.0 [CI SKIP] 2022-09-04 17:30:36 +00:00
AAGaming
076d9eb5e8 feat(patcher): rewrite to support multiple patches
BREAKING CHANGE: All usage of *Patch functions must now store the result and call .unpatch()
unpatch() has been removed.
2022-09-04 13:29:36 -04:00
semantic-release-bot
f66f5dd794 chore(release): 1.8.3 [CI SKIP] 2022-09-03 04:36:42 +00:00
botato
d01c7b3904 fix(plugin): Export ServerResponse for use in plugin-loader.tsx (#20) 2022-09-02 21:35:59 -07:00
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
50 changed files with 8215 additions and 23334 deletions

54
.github/workflows/docs.yaml vendored Normal file
View File

@@ -0,0 +1,54 @@
name: Generate docs
on:
push:
branches:
- main
jobs:
release:
name: Generate Docs
runs-on: ubuntu-22.04
steps:
- name: Setup | Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
path: lib
- name: Setup | Checkout wiki
uses: actions/checkout@v3
with:
repository: SteamDeckHomebrew/wiki
path: wiki
ssh-key: ${{ secrets.SSH_DEPLOY_KEY }}
persist-credentials: true
- name: Setup | Node.js
uses: actions/setup-node@v3
with:
node-version: 18
- name: Setup | Dependencies
run: |
cd lib
npm i -g pnpm
pnpm i --frozen-lockfile
- name: Build Docs
run: |
cd lib
pnpm run docs --out ../wiki/api-docs/decky-frontend-lib
- name: Commit files
run: |
cd wiki
git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
git add -A ./api-docs/decky-frontend-lib
git commit -m "Update decky-frontend-lib API docs"
- name: Push changes
uses: ad-m/github-push-action@master
with:
ssh: true
directory: ./wiki
repository: SteamDeckHomebrew/wiki
branch: main

View File

@@ -1,6 +1,7 @@
name: Release name: Release
on: on:
pull_request:
push: push:
branches: branches:
- main - main
@@ -19,11 +20,14 @@ jobs:
with: with:
node-version: 16 node-version: 16
- name: Setup | Dependencies - name: Setup | Dependencies
run: npm ci run: npm i -g pnpm && pnpm i --frozen-lockfile
- name: Build
run: pnpm run build
- name: Test - name: Test
run: npm test run: pnpm run test
- name: Release - name: Release
if: github.event_name != 'pull_request'
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: npm exec semantic-release run: pnpm exec semantic-release

3
.gitignore vendored
View File

@@ -36,5 +36,4 @@ dist/
research/ research/
# PNPM lockfile docs/
pnpm-lock.yaml

View File

@@ -1,7 +1,16 @@
{ {
"branches": ["main", "dev"], "branches": ["main", "dev"],
"plugins": [ "plugins": [
"@semantic-release/commit-analyzer", [
"@semantic-release/commit-analyzer",
{
"preset": "angular",
"releaseRules": [
{"type": "chore", "scope": "classes", "release": "patch"},
{"type": "*", "scope": "docs", "release": false}
]
}
],
"@semantic-release/release-notes-generator", "@semantic-release/release-notes-generator",
"@semantic-release/changelog", "@semantic-release/changelog",
"@semantic-release/npm", "@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,552 @@
## [3.18.2](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.18.1...v3.18.2) (2022-12-11)
### Bug Fixes
* **useQuickAccessVisible:** remove invalid prop access ([#66](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/66)) ([767dc2f](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/767dc2fcee97d8b6c2d331ae29704d9b469de51a))
## [3.18.1](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.18.0...v3.18.1) (2022-12-11)
### Bug Fixes
* **findSP:** fallback to last active context ([#53](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/53)) ([6f14da1](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/6f14da152acc4757b814844f1b77bf83dd98d77e))
# [3.18.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.17.0...v3.18.0) (2022-12-11)
### Features
* **DialogCheckbox:** Add DialogCheckbox component ([#58](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/58)) ([88f245d](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/88f245d476a6477e9fc0cd35e9b675961ecbc26c))
# [3.17.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.16.2...v3.17.0) (2022-12-11)
### Features
* **ControlsList:** Add ControlsList component ([#61](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/61)) ([c586afb](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/c586afb97d59928ecb703b5a254ed1b9405e2c7e))
## [3.16.2](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.16.1...v3.16.2) (2022-12-11)
### Bug Fixes
* **Marquee:** replace default export with named export ([cd0635e](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/cd0635e94f98499f9f5fc24a7fd4b93efe7dfc38))
## [3.16.1](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.16.0...v3.16.1) (2022-12-11)
### Bug Fixes
* **FooterLegend:** change description types to ReactNode ([#62](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/62)) ([d24136e](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/d24136ecb6b0c5239b68723e8f92a4822aa7b590))
# [3.16.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.15.0...v3.16.0) (2022-12-11)
### Features
* **Marquee:** Add Marquee component ([#63](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/63)) ([925ea8c](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/925ea8c3ceaaf6ff2f79b8808908a9b144a4fcff))
# [3.15.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.14.0...v3.15.0) (2022-12-11)
### Features
* **Focusable:** add noFocusRing prop type ([#65](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/65)) ([cc29dda](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/cc29ddaf578e21ab2abe1cd266f1d15debee0637))
# [3.14.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.13.0...v3.14.0) (2022-12-10)
### Features
* **toast:** add showToast/playSound to ToastData ([#64](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/64)) ([7ba1229](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/7ba1229a4e24fea587b96dc8b078200faf45ddee))
# [3.13.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.12.0...v3.13.0) (2022-11-29)
### Features
* **Menu:** add more missing props ([#60](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/60)) [CI SKIP] ([678ba21](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/678ba216f1e194986b0c391398e6f73536cd0102))
# [3.12.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.11.1...v3.12.0) (2022-11-28)
### Features
* **MenuItem:** add missing props ([#59](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/59)) ([c84a091](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/c84a09146935f0942265b7a1e4aadc40e8cf22dc))
## [3.11.1](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.11.0...v3.11.1) (2022-11-20)
### Bug Fixes
* **Footer:** add types for ActionDescriptionMap ([2ec9519](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/2ec9519b7d6d1cc0d232853ce05a773953b37c5a))
# [3.11.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.10.0...v3.11.0) (2022-11-18)
### Features
* **classes:** add "appDetailsClasses" ([#55](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/55)) ([ed98d14](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/ed98d14b37cf09500afd88e7c8e9c03749119b38))
# [3.10.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.9.0...v3.10.0) (2022-11-18)
### Features
* **classes:** add appDetailsHeaderClasses ([#54](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/54)) ([3229162](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/32291620b403f8b65cf378343454a3f2668fb6ee))
# [3.9.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.8.0...v3.9.0) (2022-11-16)
### Features
* **Dialog:** add "focusable" button prop ([#51](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/51)) ([e167ef5](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/e167ef5a138a3edc004db2365334f8455c177132))
# [3.8.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.7.14...v3.8.0) (2022-11-11)
### Features
* **routerhook:** add global components support ([215156d](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/215156d31688faac9028627379e5a3ac4d64ec46))
## [3.7.14](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.7.13...v3.7.14) (2022-11-05)
### Bug Fixes
* **Menu:** fix on Steam beta ([82768e0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/82768e0415d084deb2af39beb3f9273a83e819de))
* **Modal:** fix on Steam beta ([e44187f](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/e44187fe4b9d3e3c9e94490669591599dc5246ba))
## [3.7.13](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.7.12...v3.7.13) (2022-11-02)
### Bug Fixes
* **useQuickAccessVisible:** make it work in beta ([#49](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/49)) ([e1f64a3](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/e1f64a38de85073e5cea74ecea4b9cde9a783ecc))
## [3.7.12](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.7.11...v3.7.12) (2022-10-29)
### Bug Fixes
* **Item:** change title and description types to ReactNode ([0ed054f](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/0ed054fae972ffd36299b142bd693f80388480a6))
## [3.7.11](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.7.10...v3.7.11) (2022-10-28)
### Bug Fixes
* **package.json:** train wtf ([789e163](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/789e16380fd01a6b46188c7a1174a55c18c8dead))
## [3.7.10](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.7.9...v3.7.10) (2022-10-28)
### Bug Fixes
* **tabs:** shut up typescript ([75f3588](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/75f35882f27252e1255208953a6e801c68d5dcec))
## [3.7.9](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.7.8...v3.7.9) (2022-10-28)
### Bug Fixes
* **tabs:** fix on stable for real this time i think ([a074277](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/a074277bb58428a64295154ebf96aceb96e654a7))
## [3.7.8](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.7.7...v3.7.8) (2022-10-26)
### Bug Fixes
* **Field:** fix this time for real ([#44](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/44)) ([cfef1dc](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/cfef1dc320a5f649d66c3af365cd6aa2d88e46ea))
## [3.7.7](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.7.6...v3.7.7) (2022-10-26)
### Bug Fixes
* **Field:** remove incompatible properties ([#42](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/42)) ([0010a1f](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/0010a1fceedc417aa25b709d066341da97d42444))
## [3.7.6](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.7.5...v3.7.6) (2022-10-26)
### Bug Fixes
* **Field:** add override for onClick type ([#43](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/43)) ([fe75dfb](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/fe75dfb5f4fb1ec9417cc07dc714c71820945748))
## [3.7.5](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.7.4...v3.7.5) (2022-10-26)
### Bug Fixes
* **Field:** add types for focusing field ([#41](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/41)) ([bedb6b8](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/bedb6b8bb90e021a60e47a93709d6f48e0bd75c6))
## [3.7.4](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.7.3...v3.7.4) (2022-10-26)
### Bug Fixes
* **docs:** change arg format ([ed0b92d](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/ed0b92de2ec13a585f6524b45eef0ab538d87448))
* **tabs:** fix on stable ([f16e0b2](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/f16e0b29f8e1de500e8f436db659d1ad99d4eaa6))
## [3.7.3](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.7.2...v3.7.3) (2022-10-25)
### Bug Fixes
* **tabs:** it returns ([3c553a2](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/3c553a227d1aa7b03c4431ff968f336b4f871801))
## [3.7.2](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.7.1...v3.7.2) (2022-10-24)
### Bug Fixes
* **tabs:** unkill build ([3dbca1a](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/3dbca1a0567592a597e70ce5e9bef157f709c765))
## [3.7.1](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.7.0...v3.7.1) (2022-10-24)
### Bug Fixes
* **Tabs:** temp remove until we have a way to grab it on beta ([25c33b2](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/25c33b2a05a30c3c72008c5f459c3b77f819db5a))
# [3.7.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.6.1...v3.7.0) (2022-10-24)
### Features
* **modal:** support for latest steamos preview ([5f0470c](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/5f0470c351dc4ecb24ea3e928ff0b0199c399fa4))
## [3.6.1](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.6.0...v3.6.1) (2022-10-19)
### Bug Fixes
* **plugin:** export RoutePatch ([#39](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/39)) ([c44c66f](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/c44c66facd4e158aa4fe0a69f62a2ca3add805c1))
# [3.6.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.5.6...v3.6.0) (2022-10-15)
### Features
* **plugin:** add alwaysRender ([2fc2060](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/2fc2060a6c0d9414d1c36a1a022fdc6f2cd7f8bb))
## [3.5.6](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.5.5...v3.5.6) (2022-10-08)
### Bug Fixes
* **Dialog:** remove not exported dialog button ([#37](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/37)) ([5a5218a](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/5a5218a7c43f6a90fc4de5f7a0cd524d1cd298d6))
## [3.5.5](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.5.4...v3.5.5) (2022-10-08)
### Bug Fixes
* **sidebarnavigation:** no dont ([0ce1b54](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/0ce1b5499df699f602aa83ab87ad8b246d133eac))
## [3.5.4](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.5.3...v3.5.4) (2022-10-08)
### Bug Fixes
* **sidebarnavigation:** allow null pags ([d6b00b0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/d6b00b07337f7a9d38813eeec7c0a848d5c15f17))
## [3.5.3](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.5.2...v3.5.3) (2022-10-08)
### Bug Fixes
* **tabs:** fix props and add example ([4024b76](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/4024b76918eea43e43a24c162a937877f18627f0))
## [3.5.2](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.5.1...v3.5.2) (2022-10-08)
### Bug Fixes
* **Tabs:** make onShowTab required ([7161e75](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/7161e757e9c98d677510e03eb2606ce58152f3b1))
## [3.5.1](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.5.0...v3.5.1) (2022-10-08)
### Bug Fixes
* **Tabs:** actually export it lmao ([0e0e0d2](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/0e0e0d204adc8d888f05e98edb6c1a1a171d00bb))
# [3.5.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.4.0...v3.5.0) (2022-10-08)
### Features
* **Tabs:** initial tabs component, props, docs ([abbd3cd](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/abbd3cddae24039cbc9b7d955924431e8fbacf94))
# [3.4.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.3.5...v3.4.0) (2022-10-06)
### Features
* **hooks:** Added useParams hook ([#36](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/36)) ([e2920dd](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/e2920dd91e81d915a2319280d8473df71a4e4232))
## [3.3.5](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.3.4...v3.3.5) (2022-10-02)
### Bug Fixes
* **docs:** set categorizeByGroup to true ([0f205e8](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/0f205e891694e2cee211b0c2db74a6dda2432507))
## [3.3.4](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.3.3...v3.3.4) (2022-10-02)
### Bug Fixes
* **docs:** build each component as a seperate page ([fbd936d](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/fbd936dc1fe4c23c72f4ee27af95abc004382acd))
## [3.3.3](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.3.2...v3.3.3) (2022-10-02)
### Bug Fixes
* **modal:** make children optional ([#34](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/34)) ([99ad754](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/99ad7543a9966b8ff3f4ec01e6f05c94e5242c93))
## [3.3.2](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.3.1...v3.3.2) (2022-10-02)
### Bug Fixes
* **modal:** allow children ([40871af](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/40871af8539858f435c83123a56d4b31b63d627d))
## [3.3.1](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.3.0...v3.3.1) (2022-10-02)
### Bug Fixes
* **SidebarNavigation:** add more props ([#29](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/29)) ([ed0be5e](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/ed0be5e87e964ed57cc99b40ff55fe35a2f518b2))
# [3.3.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.2.2...v3.3.0) (2022-10-02)
### Features
* **Menu:** add nested menu groups + more props ([#30](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/30)) ([4233128](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/4233128c7ee8c6e5ab4ee74385c7b1b911d507a6))
## [3.2.2](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.2.1...v3.2.2) (2022-09-29)
### Bug Fixes
* **modal:** extend props for modals ([#32](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/32)) ([1fbe55a](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/1fbe55aa544c9e84e2b3e2d6af9950db2fe7546c))
## [3.2.1](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.2.0...v3.2.1) (2022-09-24)
### Bug Fixes
* **modal:** update showModal types ([#27](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/27)) ([6996e54](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/6996e5424f33467ef5bb93f47614058c127cb3ee))
# [3.2.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.1.4...v3.2.0) (2022-09-20)
### Features
* **FooterLegend:** add GamepadEvent ([130dfa2](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/130dfa24c51c3a670cca9ebc38e4891618532bef))
## [3.1.4](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.1.3...v3.1.4) (2022-09-19)
### Bug Fixes
* **License:** update license in package.json ([064c161](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/064c161b6736bb5574f28cb986c5899620fd69fe))
## [3.1.3](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.1.2...v3.1.3) (2022-09-18)
### Bug Fixes
* **DialogButton:** fix ref type ([74aefc7](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/74aefc7b3c19a98fb607e78c4063c098a2e12546))
## [3.1.2](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.1.1...v3.1.2) (2022-09-18)
### Bug Fixes
* **DialogButton:** fix types, add ref types ([db41e74](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/db41e74cf9f584301e59556e64a5c2371df18ed0))
## [3.1.1](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.1.0...v3.1.1) (2022-09-18)
### Bug Fixes
* **Button:** add types to DialogButton ([29201fa](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/29201fadafff6bdaa0f8fe7d7806cfa88ec545ab))
# [3.1.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.0.0...v3.1.0) (2022-09-18)
### Features
* **components:** add FocusRing ([b05f846](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/b05f84658a1b91bd260f0acd7d0984c2ed714e06))
# [3.0.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v2.0.0...v3.0.0) (2022-09-09)
### Bug Fixes
* **button:** add style prop ([d6a08fe](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/d6a08feca0f7c42e88b4d227b2953a28ac6c424d))
* **textfield:** extend HTMLAttributes ([71c7afa](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/71c7afa1a641b6651e6e73ff5575b665e5e3c48e))
### Features
* **modal:** add more props, refactor ([26017e7](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/26017e7de4600cc677a8a1e0881f2e58b3d5fe65))
* **serverAPI:** add FilePicker ([8eb921e](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/8eb921e8b787a8e5045badff58cd9a1a54038692))
### BREAKING CHANGES
* **modal:** ModalRoot ->ConfirmModal
add the actual ModalRoot which does not contain buttons
# [2.0.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.8.3...v2.0.0) (2022-09-04)
### Features
* **patcher:** rewrite to support multiple patches ([076d9eb](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/076d9eb5e8f22bfa49afc242608698da2ded50e4))
### BREAKING CHANGES
* **patcher:** All usage of *Patch functions must now store the result and call .unpatch()
unpatch() has been removed.
## [1.8.3](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.8.2...v1.8.3) (2022-09-03)
### Bug Fixes
* **plugin:** Export ServerResponse for use in plugin-loader.tsx ([#20](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/20)) ([d01c7b3](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/d01c7b3904c12142a58f78cbb93a4c1ecb438280))
## [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) # [1.3.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.2.4...v1.3.0) (2022-08-02)

686
LICENSE
View File

@@ -1,223 +1,398 @@
GNU GENERAL PUBLIC LICENSE GNU LESSER GENERAL PUBLIC LICENSE
Version 2, June 1991 Version 2.1, February 1999
Copyright (C) 1989, 1991 Free Software Foundation, Inc., Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed. of this license document, but changing it is not allowed.
Preamble [This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
The licenses for most software are designed to take away your Preamble
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not The licenses for most software are designed to take away your
price. Our General Public Licenses are designed to make sure that you freedom to share and change it. By contrast, the GNU General Public
have the freedom to distribute copies of free software (and charge for Licenses are intended to guarantee your freedom to share and change
this service if you wish), that you receive source code or can get it free software--to make sure the software is free for all its users.
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid This license, the Lesser General Public License, applies to some
anyone to deny you these rights or to ask you to surrender the rights. specially designated software packages--typically libraries--of the
These restrictions translate to certain responsibilities for you if you Free Software Foundation and other authors who decide to use it. You
distribute copies of the software, or if you modify it. can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
For example, if you distribute copies of such a program, whether When we speak of free software, we are referring to freedom of use,
gratis or for a fee, you must give the recipients all the rights that not price. Our General Public Licenses are designed to make sure that
you have. You must make sure that they, too, receive or can get the you have the freedom to distribute copies of free software (and charge
source code. And you must show them these terms so they know their for this service if you wish); that you receive source code or can get
rights. it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
We protect your rights with two steps: (1) copyright the software, and To protect your rights, we need to make restrictions that forbid
(2) offer you this license which gives you legal permission to copy, distributors to deny you these rights or to ask you to surrender these
distribute and/or modify the software. rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
Also, for each author's protection and ours, we want to make certain For example, if you distribute copies of the library, whether gratis
that everyone understands that there is no warranty for this free or for a fee, you must give the recipients all the rights that we gave
software. If the software is modified by someone else and passed on, we you. You must make sure that they, too, receive or can get the source
want its recipients to know that what they have is not the original, so code. If you link other code with the library, you must provide
that any problems introduced by others will not reflect on the original complete object files to the recipients, so that they can relink them
authors' reputations. with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
Finally, any free program is threatened constantly by software We protect your rights with a two-step method: (1) we copyright the
patents. We wish to avoid the danger that redistributors of a free library, and (2) we offer you this license, which gives you legal
program will individually obtain patent licenses, in effect making the permission to copy, distribute and/or modify the library.
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and To protect each distributor, we want to make it very clear that
modification follow. there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
GNU GENERAL PUBLIC LICENSE Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
0. This License applies to any program or other work which contains When a program is linked with a library, whether statically or using
a notice placed by the copyright holder saying it may be distributed a shared library, the combination of the two is legally speaking a
under the terms of this General Public License. The "Program", below, combined work, a derivative of the original library. The ordinary
refers to any such program or work, and a "work based on the Program" General Public License therefore permits such linking only if the
means either the Program or any derivative work under copyright law: entire combination fits its criteria of freedom. The Lesser General
that is to say, a work containing the Program or a portion of it, Public License permits more lax criteria for linking other code with
either verbatim or with modifications and/or translated into another the library.
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not We call this license the "Lesser" General Public License because it
covered by this License; they are outside its scope. The act of does Less to protect the user's freedom than the ordinary General
running the Program is not restricted, and the output from the Program Public License. It also provides other free software developers Less
is covered only if its contents constitute a work based on the of an advantage over competing non-free programs. These disadvantages
Program (independent of having been made by running the Program). are the reason we use the ordinary General Public License for many
Whether that is true depends on what the Program does. libraries. However, the Lesser license provides advantages in certain
special circumstances.
1. You may copy and distribute verbatim copies of the Program's For example, on rare occasions, there may be a special need to
source code as you receive it, in any medium, provided that you encourage the widest possible use of a certain library, so that it becomes
conspicuously and appropriately publish on each copy an appropriate a de-facto standard. To achieve this, non-free programs must be
copyright notice and disclaimer of warranty; keep intact all the allowed to use the library. A more frequent case is that a free
notices that refer to this License and to the absence of any warranty; library does the same job as widely used non-free libraries. In this
and give any other recipients of the Program a copy of this License case, there is little to gain by limiting the free library to free
along with the Program. software only, so we use the Lesser General Public License.
You may charge a fee for the physical act of transferring a copy, and In other cases, permission to use a particular library in non-free
you may at your option offer warranty protection in exchange for a fee. programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
2. You may modify your copy or copies of the Program or any portion Although the Lesser General Public License is Less protective of the
of it, thus forming a work based on the Program, and copy and users' freedom, it does ensure that the user of a program that is
distribute such modifications or work under the terms of Section 1 linked with the Library has the freedom and the wherewithal to run
above, provided that you also meet all of these conditions: that program using a modified version of the Library.
a) You must cause the modified files to carry prominent notices The precise terms and conditions for copying, distribution and
stating that you changed the files and the date of any change. modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
b) You must cause any work that you distribute or publish, that in GNU LESSER GENERAL PUBLIC LICENSE
whole or in part contains or is derived from the Program or any TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively 0. This License Agreement applies to any software library or other
when run, you must cause it, when started running for such program which contains a notice placed by the copyright holder or
interactive use in the most ordinary way, to print or display an other authorized party saying it may be distributed under the terms of
announcement including an appropriate copyright notice and a this Lesser General Public License (also called "this License").
notice that there is no warranty (or else, saying that you provide Each licensee is addressed as "you".
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If A "library" means a collection of software functions and/or data
identifiable sections of that work are not derived from the Program, prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it. entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or exercise the right to control the distribution of derivative or
collective works based on the Program. collective works based on the Library.
In addition, mere aggregation of another work not based on the Program In addition, mere aggregation of another work not based on the Library
with the Program (or with a work based on the Program) on a volume of with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under a storage or distribution medium does not bring the other work under
the scope of this License. the scope of this License.
3. You may copy and distribute the Program (or a work based on it, 3. You may opt to apply the terms of the ordinary GNU General Public
under Section 2) in object code or executable form under the terms of License instead of this License to a given copy of the Library. To do
Sections 1 and 2 above provided that you also do one of the following: this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
a) Accompany it with the complete corresponding machine-readable Once this change is made in a given copy, it is irreversible for
source code, which must be distributed under the terms of Sections that copy, so the ordinary GNU General Public License applies to all
1 and 2 above on a medium customarily used for software interchange; or, subsequent copies and derivative works made from that copy.
b) Accompany it with a written offer, valid for at least three This option is useful when you wish to copy part of the code of
years, to give any third party, for a charge no more than your the Library into a program that is not a library.
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer 4. You may copy and distribute the Library (or a portion or
to distribute corresponding source code. (This alternative is derivative of it, under Section 2) in object code or executable form
allowed only for noncommercial distribution and only if you under the terms of Sections 1 and 2 above provided that you accompany
received the program in object code or executable form with such it with the complete corresponding machine-readable source code, which
an offer, in accord with Subsection b above.) must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
The source code for a work means the preferred form of the work for If distribution of object code is made by offering access to copy
making modifications to it. For an executable work, complete source from a designated place, then offering equivalent access to copy the
code means all the source code for all modules it contains, plus any source code from the same place satisfies the requirement to
associated interface definition files, plus the scripts used to distribute the source code, even though third parties are not
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code. compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program 5. A program that contains no derivative of any portion of the
except as expressly provided under this License. Any attempt Library, but is designed to work with the Library by being compiled or
otherwise to copy, modify, sublicense or distribute the Program is linked with it, is called a "work that uses the Library". Such a
void, and will automatically terminate your rights under this License. work, in isolation, is not a derivative work of the Library, and
However, parties who have received copies, or rights, from you under therefore falls outside the scope of this License.
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not However, linking a "work that uses the Library" with the Library
signed it. However, nothing else grants you permission to modify or creates an executable that is a derivative of the Library (because it
distribute the Program or its derivative works. These actions are contains portions of the Library), rather than a "work that uses the
prohibited by law if you do not accept this License. Therefore, by library". The executable is therefore covered by this License.
modifying or distributing the Program (or any work based on the Section 6 states terms for distribution of such executables.
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the When a "work that uses the Library" uses material from a header file
Program), the recipient automatically receives a license from the that is part of the Library, the object code for the work may be a
original licensor to copy, distribute or modify the Program subject to derivative work of the Library even though the source code is not.
these terms and conditions. You may not impose any further Whether this is true is especially significant if the work can be
restrictions on the recipients' exercise of the rights granted herein. linked without the Library, or if the work is itself a library. The
You are not responsible for enforcing compliance by third parties to threshold for this to be true is not precisely defined by law.
this License.
7. If, as a consequence of a court judgment or allegation of patent If such an object file uses only numerical parameters, data
infringement or for any other reason (not limited to patent issues), structure layouts and accessors, and small macros and small inline
conditions are imposed on you (whether by court order, agreement or functions (ten lines or less in length), then the use of the object
otherwise) that contradict the conditions of this License, they do not file is unrestricted, regardless of whether it is legally a derivative
excuse you from the conditions of this License. If you cannot work. (Executables containing this object code plus portions of the
distribute so as to satisfy simultaneously your obligations under this Library will still fall under Section 6.)
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under Otherwise, if the work is a derivative of the Library, you may
any particular circumstance, the balance of the section is intended to distribute the object code for the work under the terms of Section 6.
apply and the section as a whole is intended to apply in other Any executables containing that work also fall under Section 6,
circumstances. whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is integrity of the free software distribution system which is
implemented by public license practices. Many people have made implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing system; it is up to the author/donor to decide if he or she is willing
@@ -227,114 +402,103 @@ impose that choice.
This section is intended to make thoroughly clear what is believed to This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License. be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in 12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License original copyright holder who places the Library under this License may add
may add an explicit geographical distribution limitation excluding an explicit geographical distribution limitation excluding those countries,
those countries, so that distribution is permitted only in or among so that distribution is permitted only in or among countries not thus
countries not thus excluded. In such case, this License incorporates excluded. In such case, this License incorporates the limitation as if
the limitation as if written in the body of this License. written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions 13. The Free Software Foundation may publish revised and/or new
of the General Public License from time to time. Such new versions will versions of the Lesser General Public License from time to time.
be similar in spirit to the present version, but may differ in detail to Such new versions will be similar in spirit to the present version,
address new problems or concerns. but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Program Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and "any specifies a version number of this License which applies to it and
later version", you have the option of following the terms and conditions "any later version", you have the option of following the terms and
either of that version or of any later version published by the Free conditions either of that version or of any later version published by
Software Foundation. If the Program does not specify a version number of the Free Software Foundation. If the Library does not specify a
this License, you may choose any version ever published by the Free Software license version number, you may choose any version ever published by
Foundation. the Free Software Foundation.
10. If you wish to incorporate parts of the Program into other free 14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are different, write to the author programs whose distribution conditions are incompatible with these,
to ask for permission. For software which is copyrighted by the Free write to the author to ask for permission. For software which is
Software Foundation, write to the Free Software Foundation; we sometimes copyrighted by the Free Software Foundation, write to the Free
make exceptions for this. Our decision will be guided by the two goals Software Foundation; we sometimes make exceptions for this. Our
of preserving the free status of all derivatives of our free software and decision will be guided by the two goals of preserving the free status
of promoting the sharing and reuse of software generally. of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
REPAIR OR CORRECTION. THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
POSSIBILITY OF SUCH DAMAGES. SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs How to Apply These Terms to Your New Libraries
If you develop a new program, and you want it to be of the greatest If you develop a new library, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it possible use to the public, we recommend making it free software that
free software which everyone can redistribute and change under these terms. everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To do so, attach the following notices to the program. It is safest To apply these terms, attach the following notices to the library. It is
to attach them to the start of each source file to most effectively safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least convey the exclusion of warranty; and each file should have at least the
the "copyright" line and a pointer to where the full notice is found. "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.> <one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author> Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify This library is free software; you can redistribute it and/or
it under the terms of the GNU General Public License as published by modify it under the terms of the GNU Lesser General Public
the Free Software Foundation; either version 2 of the License, or License as published by the Free Software Foundation; either
(at your option) any later version. version 2.1 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
GNU General Public License for more details. Lesser General Public License for more details.
You should have received a copy of the GNU General Public License along You should have received a copy of the GNU Lesser General Public
with this program; if not, write to the Free Software Foundation, Inc., License along with this library; if not, write to the Free Software
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
USA
Also add information on how to contact you by electronic and paper mail. Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names: necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program Yoyodyne, Inc., hereby disclaims all copyright interest in the
`Gnomovision' (which makes passes at compilers) written by James Hacker. library `Frob' (a library for tweaking knobs) written by James Random
Hacker.
<signature of Ty Coon>, 1 April 1989 <signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice Ty Coon, President of Vice
This General Public License does not permit incorporating your program into That's all there is to it!
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

22846
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{ {
"name": "decky-frontend-lib", "name": "decky-frontend-lib",
"version": "1.3.0", "version": "3.18.2",
"description": "A library for building decky plugins", "description": "A library for building decky plugins",
"main": "dist/index.js", "main": "dist/index.js",
"types": "dist/index.d.ts", "types": "dist/index.d.ts",
@@ -9,7 +9,7 @@
"scripts": { "scripts": {
"build": "shx rm -rf dist && tsc -b", "build": "shx rm -rf dist && tsc -b",
"dev": "tsc -b -w", "dev": "tsc -b -w",
"prepack": "npm run build", "docs": "typedoc --tsconfig ./tsconfig.json src/**/*",
"test": "echo 'No tests for now!'", "test": "echo 'No tests for now!'",
"prepare": "husky install", "prepare": "husky install",
"commit": "git-cz" "commit": "git-cz"
@@ -30,7 +30,7 @@
"components" "components"
], ],
"author": "Jonas Dellinger <jonas@dellinger.dev>", "author": "Jonas Dellinger <jonas@dellinger.dev>",
"license": "GPL-2.0-or-later", "license": "LGPL-2.1",
"bugs": { "bugs": {
"url": "https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues" "url": "https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues"
}, },
@@ -53,10 +53,16 @@
"husky": "^8.0.1", "husky": "^8.0.1",
"import-sort-style-module": "^6.0.0", "import-sort-style-module": "^6.0.0",
"jest": "^27.5.1", "jest": "^27.5.1",
"minimist": "^1.2.6",
"prettier": "^2.7.1",
"prettier-plugin-import-sort": "^0.0.7", "prettier-plugin-import-sort": "^0.0.7",
"semantic-release": "^19.0.2", "semantic-release": "^19.0.3",
"shx": "^0.3.4", "shx": "^0.3.4",
"ts-jest": "^27.1.4", "ts-jest": "^27.1.4",
"typedoc": "^0.23.15",
"typedoc-plugin-markdown": "^3.13.6",
"typedoc-plugin-mdn-links": "^2.0.0",
"typedoc-plugin-missing-exports": "^1.0.0",
"typescript": "^4.6.3" "typescript": "^4.6.3"
}, },
"pnpm": { "pnpm": {

5283
pnpm-lock.yaml generated Executable file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,131 @@
import { CSSProperties, FC, useState } from 'react';
import { ConfirmModal, SliderField, gamepadSliderClasses } from '../deck-components';
interface ColorPickerModalProps {
closeModal: () => void;
onConfirm?(HSLString: string): any;
title?: string;
defaultH?: number;
defaultS?: number;
defaultL?: number;
defaultA?: number;
}
export const ColorPickerModal: FC<ColorPickerModalProps> = ({
closeModal,
onConfirm = () => {},
title = 'Color Picker',
defaultH = 0,
defaultS = 100,
defaultL = 50,
defaultA = 1,
}) => {
const [H, setH] = useState<number>(defaultH);
const [S, setS] = useState<number>(defaultS);
const [L, setL] = useState<number>(defaultL);
const [A, setA] = useState<number>(defaultA);
const colorPickerCSSVars = {
'--decky-color-picker-hvalue': `${H}`,
'--decky-color-picker-svalue': `${S}%`,
'--decky-color-picker-lvalue': `${L}%`,
'--decky-color-picker-avalue': `${A}`,
} as CSSProperties;
return (
<ConfirmModal
bAllowFullSize
onCancel={closeModal}
onOK={() => {
onConfirm(`hsla(${H}, ${S}%, ${L}%, ${A})`);
closeModal();
}}
>
<style>
{`
/* This removes the cyan track color that is behind the slider head */
.ColorPicker_Container .${gamepadSliderClasses.SliderTrack} {
--left-track-color: #0000;
/* This is for compatibility with the "Colored Toggles" CSSLoader Theme*/
--colored-toggles-main-color: #0000;
}
.ColorPicker_HSlider .${gamepadSliderClasses.SliderTrack} {
background: linear-gradient(
270deg,
hsla(360, var(--decky-color-picker-svalue), var(--decky-color-picker-lvalue), var(--decky-color-picker-avalue)),
hsla(270, var(--decky-color-picker-svalue), var(--decky-color-picker-lvalue), var(--decky-color-picker-avalue)),
hsla(180, var(--decky-color-picker-svalue), var(--decky-color-picker-lvalue), var(--decky-color-picker-avalue)),
hsla(90, var(--decky-color-picker-svalue), var(--decky-color-picker-lvalue), var(--decky-color-picker-avalue)),
hsla(0, var(--decky-color-picker-svalue), var(--decky-color-picker-lvalue), var(--decky-color-picker-avalue))
);
}
.ColorPicker_SSlider .${gamepadSliderClasses.SliderTrack} {
background: linear-gradient(
90deg,
hsla(var(--decky-color-picker-hvalue), 0%, var(--decky-color-picker-lvalue), var(--decky-color-picker-avalue)),
hsla(var(--decky-color-picker-hvalue), 100%, var(--decky-color-picker-lvalue), var(--decky-color-picker-avalue))
);
}
.ColorPicker_LSlider .${gamepadSliderClasses.SliderTrack} {
background: linear-gradient(
90deg,
hsla(var(--decky-color-picker-hvalue), var(--decky-color-picker-svalue), 0%, var(--decky-color-picker-avalue)),
hsla(var(--decky-color-picker-hvalue), var(--decky-color-picker-svalue), 50%, var(--decky-color-picker-avalue)),
hsla(var(--decky-color-picker-hvalue), var(--decky-color-picker-svalue), 100%, var(--decky-color-picker-avalue))
);
}
.ColorPicker_ASlider .${gamepadSliderClasses.SliderTrack} {
background: linear-gradient(
90deg,
hsla(var(--decky-color-picker-hvalue), var(--decky-color-picker-svalue), var(--decky-color-picker-lvalue), 0),
hsla(var(--decky-color-picker-hvalue), var(--decky-color-picker-svalue), var(--decky-color-picker-lvalue), 1)
);
}
`}
</style>
<div
className="ColorPicker_ColorDisplayContainer"
style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: '1em',
// theres a large header by default on the modal, so this just pushes it up into that unused space
marginTop: '-2.5em',
}}
>
<div>
<span style={{ fontSize: '1.5em' }}>
<b>{title}</b>
</span>
</div>
<div
style={{
backgroundColor: `hsla(${H}, ${S}%, ${L}%, ${A})`,
width: '40px',
height: '40px',
}}
></div>
</div>
<div className="ColorPicker_Container" style={colorPickerCSSVars}>
<div className="ColorPicker_HSlider">
<SliderField showValue editableValue label="Hue" value={H} min={0} max={360} onChange={setH} />
</div>
<div className="ColorPicker_SSlider">
<SliderField showValue editableValue label="Saturation" value={S} min={0} max={100} onChange={setS} />
</div>
<div className="ColorPicker_LSlider">
<SliderField showValue editableValue label="Lightness" value={L} min={0} max={100} onChange={setL} />
</div>
<div className="ColorPicker_ASlider">
<SliderField showValue editableValue label="Alpha" value={A} step={0.1} min={0} max={1} onChange={setA} />
</div>
</div>
</ConfirmModal>
);
};

View File

@@ -1,7 +1,8 @@
import { Spinner } from '../deck-components';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { FC, ImgHTMLAttributes, useState } from 'react'; import { FC, ImgHTMLAttributes, useState } from 'react';
import { Spinner } from '../deck-components';
interface SuspensefulImageProps extends ImgHTMLAttributes<HTMLImageElement> { interface SuspensefulImageProps extends ImgHTMLAttributes<HTMLImageElement> {
suspenseWidth?: string | number; suspenseWidth?: string | number;
suspenseHeight?: string | number; suspenseHeight?: string | number;
@@ -38,4 +39,4 @@ export const SuspensefulImage: FC<SuspensefulImageProps> = (props) => {
) : ( ) : (
<img {...props} /> <img {...props} />
); );
}; };

View File

@@ -1 +1,2 @@
export * from './SuspensefulImage'; export * from './SuspensefulImage';
export * from './ColorPickerModal';

View File

@@ -0,0 +1 @@
export * from './useQuickAccessVisible';

View File

@@ -0,0 +1,77 @@
import { useEffect, useState } from 'react';
declare global {
var FocusNavController: any;
}
function getQuickAccessWindow(): Window | null {
try {
const context = FocusNavController?.m_ActiveContext || FocusNavController?.m_LastActiveContext;
const navTrees = context?.m_rgGamepadNavigationTrees || FocusNavController?.m_rgGamepadNavigationTrees;
return navTrees?.find((tree: any) => tree?.id === "QuickAccess-NA")?.m_Root?.m_element?.ownerDocument.defaultView ?? null;
} catch (error) {
console.error(error);
return null;
}
}
/**
* Returns state indicating the visibility of quick access menu.
*
* @remarks
* During development it is possible to open the quick access menu without giving it
* focus in some cases. In such cases, the quick access menu state is invisible.
*
* This seems to be impossible to replicate when running the deck normally. Even in
* the edge cases it always seems to have a focus.
*
* @returns `true` if quick access menu is visible (focused) and `false` otherwise.
*
* @example
* import { VFC, useEffect } from "react";
* import { useQuickAccessVisible } from "decky-frontend-lib";
*
* export const PluginPanelView: VFC<{}> = ({ }) => {
* const isVisible = useQuickAccessVisible();
*
* useEffect(() => {
* if (!isVisible) {
* return;
* }
*
* const interval = setInterval(() => console.log("Hello world!"), 1000);
* return () => {
* clearInterval(interval);
* }
* }, [isVisible])
*
* return (
* <div>
* {isVisible ? "VISIBLE" : "INVISIBLE"}
* </div>
* );
* };
*/
export function useQuickAccessVisible(): boolean {
const [isVisible, setIsVisible] = useState(getQuickAccessWindow()?.document.hasFocus() ?? true);
useEffect(() => {
const quickAccessWindow = getQuickAccessWindow();
if (quickAccessWindow === null) {
console.error('Could not get window of QuickAccess menu!');
return;
}
const onBlur = () => setIsVisible(false);
const onFocus = () => setIsVisible(true);
quickAccessWindow.addEventListener('blur', onBlur);
quickAccessWindow.addEventListener('focus', onFocus);
return () => {
quickAccessWindow.removeEventListener('blur', onBlur);
quickAccessWindow.removeEventListener('focus', onFocus);
};
}, []);
return isVisible;
}

View File

@@ -1,29 +1,8 @@
import { FC } from 'react'; import { FC } from 'react';
import { CommonUIModule } from '../webpack'; import { DialogButton, DialogButtonProps } from './Dialog';
export interface ButtonProps { export interface ButtonProps extends DialogButtonProps {}
className?: string;
noFocusRing?: boolean;
disabled?: boolean;
onClick?(e: MouseEvent): void;
onPointerDown?(e: PointerEvent): void;
onPointerUp?(e: PointerEvent): void;
onPointerCancel?(e: PointerEvent): void;
onMouseDown?(e: PointerEvent): void;
onMouseUp?(e: MouseEvent): void;
onTouchStart?(e: TouchEvent): void;
onTouchEnd?(e: TouchEvent): void;
onTouchCancel?(e: TouchEvent): void;
onSubmit?(e: SubmitEvent): void;
}
export const DialogButton = Object.values(CommonUIModule).find(
(mod: any) =>
mod?.render?.toString()?.includes('Object.assign({type:"button"') &&
mod?.render?.toString()?.includes('DialogButton'),
) as any;
// Button isn't exported, so call DialogButton to grab it // Button isn't exported, so call DialogButton to grab it
export const Button = (DialogButton as any)?.render({}).type as FC<ButtonProps>;
export const Button = DialogButton!.render({}).type as FC<ButtonProps>; // its actually a forwarded ref but that doesn't really matter in usage

View File

@@ -8,6 +8,8 @@ export interface ButtonItemProps extends ItemProps {
disabled?: boolean; disabled?: boolean;
} }
export const ButtonItem = Object.values(CommonUIModule).find((mod: any) => export const ButtonItem = Object.values(CommonUIModule).find(
mod?.render?.toString()?.includes('childrenContainerWidth:"min"'), (mod: any) =>
mod?.render?.toString()?.includes('"highlightOnFocus","childrenContainerWidth"') ||
mod?.render?.toString()?.includes('childrenContainerWidth:"min"'),
) as FC<ButtonItemProps>; ) 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

@@ -0,0 +1,17 @@
import { findModuleChild } from '../webpack';
import { FC } from 'react';
export interface ControlsListProps {
alignItems?: 'left' | 'right' | 'center';
spacing?: 'standard' | 'extra';
}
export const ControlsList: FC<ControlsListProps> = findModuleChild((m) => {
if (typeof m !== 'object') return;
for (const prop in m) {
if (m[prop]?.toString && m[prop].toString().includes('().ControlsListChild') && m[prop].toString().includes('().ControlsListOuterPanel')) {
return m[prop];
}
}
return;
});

View File

@@ -0,0 +1,86 @@
import { CSSProperties, FC, RefAttributes } from 'react';
import { CommonUIModule } from '../webpack';
import { FooterLegendProps } from './FooterLegend';
export interface DialogCommonProps extends RefAttributes<HTMLDivElement> {
style?: CSSProperties;
className?: string;
}
export interface DialogButtonProps extends DialogCommonProps, FooterLegendProps {
/**
* Enables/disables the focus around the button.
*
* @note
* Default value depends on context, so setting it to `false` will enable it.
*/
noFocusRing?: boolean;
/**
* Disables the button - assigned `on*` methods will not be invoked if clicked.
*
* @note
* Depending on where it is, it might still get focus. In such case it can be
* partially disabled separately.
*
* @see focusable.
*/
disabled?: boolean;
/**
* Enables/disables the navigation based focus on button - you won't be able to navigate to
* it via the gamepad or keyboard.
*
* @note
* If set to `false`, it still can be clicked and **WILL** become focused until navigated away.
* Depending on the context of where the button is, even a disabled button can focused.
*/
focusable?: boolean;
onClick?(e: MouseEvent): void;
onPointerDown?(e: PointerEvent): void;
onPointerUp?(e: PointerEvent): void;
onPointerCancel?(e: PointerEvent): void;
onMouseDown?(e: MouseEvent): void;
onMouseUp?(e: MouseEvent): void;
onTouchStart?(e: TouchEvent): void;
onTouchEnd?(e: TouchEvent): void;
onTouchCancel?(e: TouchEvent): void;
onSubmit?(e: SubmitEvent): void;
}
const CommonDialogDivs = Object.values(CommonUIModule).filter(
(m: any) => typeof m === 'object' && m?.render?.toString().includes('"div",Object.assign({},'),
);
const MappedDialogDivs = new Map(
Object.values(CommonDialogDivs).map((m: any) => {
const renderedDiv = m.render({});
// Take only the first class name segment as it identifies the element we want
return [renderedDiv.props.className.split(' ')[0], m];
}),
);
export const DialogHeader = MappedDialogDivs.get('DialogHeader') as FC<DialogCommonProps>;
export const DialogSubHeader = MappedDialogDivs.get('DialogSubHeader') as FC<DialogCommonProps>;
export const DialogFooter = MappedDialogDivs.get('DialogFooter') as FC<DialogCommonProps>;
export const DialogLabel = MappedDialogDivs.get('DialogLabel') as FC<DialogCommonProps>;
export const DialogBodyText = MappedDialogDivs.get('DialogBodyText') as FC<DialogCommonProps>;
export const DialogBody = MappedDialogDivs.get('DialogBody') as FC<DialogCommonProps>;
export const DialogControlsSection = MappedDialogDivs.get('DialogControlsSection') as FC<DialogCommonProps>;
export const DialogControlsSectionHeader = MappedDialogDivs.get('DialogControlsSectionHeader') as FC<DialogCommonProps>;
export const DialogButtonPrimary = Object.values(CommonUIModule).find(
(mod: any) => mod?.render?.toString()?.includes('DialogButton') && mod?.render?.toString()?.includes('Primary'),
) as FC<DialogButtonProps>;
export const DialogButtonSecondary = Object.values(CommonUIModule).find(
(mod: any) =>
mod?.render?.toString()?.includes('Object.assign({type:"button"') &&
mod?.render?.toString()?.includes('DialogButton') &&
mod?.render?.toString()?.includes('Secondary'),
) as FC<DialogButtonProps>;
// This is the "main" button. The Primary can act as a submit button,
// therefore secondary is chosen (also for backwards comp. reasons)
export const DialogButton = DialogButtonSecondary;

View File

@@ -0,0 +1,33 @@
import { FC, ReactNode } from 'react';
import { findModule } from '../webpack';
import { DialogCommonProps } from './Dialog';
import { FooterLegendProps } from './FooterLegend';
export interface DialogCheckboxProps extends DialogCommonProps, FooterLegendProps {
onChange?(checked: boolean): void;
label?: ReactNode;
description?: ReactNode;
disabled?: boolean;
tooltip?: string;
color?: string;
highlightColor?: string;
bottomSeparator?: 'standard' | 'thick' | 'none';
controlled?: boolean;
checked?: boolean;
onClick?(evt: Event): void;
}
export const DialogCheckbox = Object.values(findModule((m: any) => {
if (typeof m !== 'object') return false;
for (const prop in m) {
if (m[prop]?.prototype?.GetPanelElementProps) return true;
}
return false;
})).find((m: any) =>
m.contextType &&
m.prototype?.render.toString().includes('fallback:') &&
m?.prototype?.SetChecked &&
m?.prototype?.Toggle &&
m?.prototype?.GetPanelElementProps
) as FC<DialogCheckboxProps>;

View File

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

View File

@@ -1,22 +1,31 @@
import { FC, HTMLAttributes, ReactNode, RefAttributes } from 'react'; import { FC, ReactNode, RefAttributes } from 'react';
import { findModuleChild } from '../webpack';
export interface FieldProps extends HTMLAttributes<HTMLDivElement> { import { findModuleChild } from '../webpack';
label?: string | ReactNode; import { FooterLegendProps } from './FooterLegend';
description?: string | ReactNode;
export interface FieldProps extends FooterLegendProps {
label?: ReactNode;
bottomSeparator?: 'standard' | 'thick' | 'none';
description?: ReactNode;
disabled?: boolean; disabled?: boolean;
icon?: ReactNode; icon?: ReactNode;
childrenLayout?: string; inlineWrap?: 'keep-inline' | 'shift-children-below'; // If label is too long it will move shildren below before starting to wrap label
childrenContainerWidth?: string; childrenLayout?: 'below' | 'inline';
padding?: string; childrenContainerWidth?: 'min' | 'max' | 'fixed'; // Does not work with childrenLayout==='below'
spacingBetweenLabelAndChild?: 'none'; // This applies only when childrenLayout==='below'
padding?: 'none' | 'standard' | 'compact';
className?: string;
highlightOnFocus?: boolean; highlightOnFocus?: boolean;
indentLevel?: number; indentLevel?: number;
verticalAlignment?: string; verticalAlignment?: 'center' | 'none'; // Alligns inline label with children
focusable?: boolean; // Allows to get focus without any focusable children or on* callbacks
onActivate?: (e: CustomEvent | MouseEvent) => void;
onClick?: (e: CustomEvent | MouseEvent) => void;
} }
export const Field = findModuleChild((m) => { export const Field = findModuleChild((m) => {
if (typeof m !== "object") return undefined; if (typeof m !== 'object') return undefined;
for (let prop in m) { for (let prop in m) {
if (m[prop]?.render?.toString().includes('"shift-children-below"')) return m[prop] if (m[prop]?.render?.toString().includes('"shift-children-below"')) return m[prop];
} }
}) as FC<FieldProps & RefAttributes<HTMLDivElement>>; }) as FC<FieldProps & RefAttributes<HTMLDivElement>>;

View File

@@ -0,0 +1,19 @@
import { ElementType, FC, ReactNode } from 'react';
import { findModuleChild } from '../webpack';
export interface FocusRingProps {
className?: string;
rootClassName?: string;
render?: ElementType;
children?: ReactNode;
NavigationManager?: any;
}
export const FocusRing = findModuleChild((m: any) => {
if (typeof m !== 'object') return false;
for (let prop in m) {
if (m[prop]?.toString()?.includes('.GetShowDebugFocusRing())')) return m[prop];
}
return false;
}) as FC<FocusRingProps>;

View File

@@ -1,11 +1,14 @@
import { HTMLAttributes, ReactNode, RefAttributes, VFC } from "react"; import { HTMLAttributes, ReactNode, RefAttributes, VFC } from 'react';
import { findModuleChild } from "../webpack";
export interface FocusableProps extends HTMLAttributes<HTMLDivElement> { import { findModuleChild } from '../webpack';
import { FooterLegendProps } from './FooterLegend';
export interface FocusableProps extends HTMLAttributes<HTMLDivElement>, FooterLegendProps {
children: ReactNode; children: ReactNode;
"flow-children"?: string; 'flow-children'?: string;
focusClassName?: string; focusClassName?: string;
focusWithinClassName?: string; focusWithinClassName?: string;
noFocusRing?: boolean;
onActivate?: (e: CustomEvent) => void; onActivate?: (e: CustomEvent) => void;
onCancel?: (e: CustomEvent) => void; onCancel?: (e: CustomEvent) => void;
} }
@@ -16,4 +19,4 @@ export const Focusable = findModuleChild((m) => {
if (m[prop]?.render?.toString()?.includes('["flow-children","onActivate","onCancel","focusClassName",')) if (m[prop]?.render?.toString()?.includes('["flow-children","onActivate","onCancel","focusClassName",'))
return m[prop]; return m[prop];
} }
}) as VFC<FocusableProps & RefAttributes<HTMLDivElement>>; }) as VFC<FocusableProps & RefAttributes<HTMLDivElement>>;

View File

@@ -0,0 +1,67 @@
import { ReactNode } from 'react';
export enum GamepadButton {
INVALID,
OK,
CANCEL,
SECONDARY,
OPTIONS,
BUMPER_LEFT,
BUMPER_RIGHT,
TRIGGER_LEFT,
TRIGGER_RIGHT,
DIR_UP,
DIR_DOWN,
DIR_LEFT,
DIR_RIGHT,
SELECT,
START,
LSTICK_CLICK,
RSTICK_CLICK,
LSTICK_TOUCH,
RSTICK_TOUCH,
LPAD_TOUCH,
LPAD_CLICK,
RPAD_TOUCH,
RPAD_CLICK,
REAR_LEFT_UPPER,
REAR_LEFT_LOWER,
REAR_RIGHT_UPPER,
REAR_RIGHT_LOWER,
STEAM_GUIDE,
STEAM_QUICK_MENU,
}
export declare enum NavEntryPositionPreferences {
FIRST,
LAST,
MAINTAIN_X,
MAINTAIN_Y,
PREFERRED_CHILD
}
export interface GamepadEventDetail {
button: number;
is_repeat?: boolean;
source: number;
}
export declare type ActionDescriptionMap = {
[key in GamepadButton]?: ReactNode
}
export declare type GamepadEvent = CustomEvent<GamepadEventDetail>;
export interface FooterLegendProps {
actionDescriptionMap?: ActionDescriptionMap;
onOKActionDescription?: ReactNode;
onCancelActionDescription?: ReactNode;
onSecondaryActionDescription?: ReactNode;
onOptionsActionDescription?: ReactNode;
onMenuActionDescription?: ReactNode;
onButtonDown?: (evt: GamepadEvent) => void;
onButtonUp?: (evt: GamepadEvent) => void;
onOKButton?: (evt: GamepadEvent) => void;
onCancelButton?: (evt: GamepadEvent) => void;
onSecondaryButton?: (evt: GamepadEvent) => void;
onOptionsButton?: (evt: GamepadEvent) => void;
onGamepadDirection?: (evt: GamepadEvent) => void;
onGamepadFocus?: (evt: GamepadEvent) => void;
onGamepadBlur?: (evt: GamepadEvent) => void;
onMenuButton?: (evt: GamepadEvent) => void;
}

View File

@@ -1,11 +1,11 @@
import { ReactNode } from 'react'; import { ReactNode } from 'react';
export interface ItemProps { export interface ItemProps {
label?: string; label?: ReactNode;
description?: string; description?: ReactNode;
layout?: 'below' | 'inline'; layout?: 'below' | 'inline';
icon?: ReactNode; icon?: ReactNode;
bottomSeparator?: boolean; bottomSeparator?: 'standard' | 'thick' | 'none';
indentLevel?: number; indentLevel?: number;
tooltip?: string; tooltip?: string;
} }

View File

@@ -0,0 +1,26 @@
import { CSSProperties, FC } from 'react';
import { findModuleChild } from '../webpack';
export interface MarqueeProps {
play?: boolean;
direction?: 'left' | 'right';
speed?: number;
delay?: number;
fadeLength?: number;
center?: boolean;
resetOnPause?: boolean;
style?: CSSProperties;
className?: string;
children: React.ReactNode;
}
export const Marquee: FC<MarqueeProps> = findModuleChild((m) => {
if (typeof m !== 'object') return;
for (const prop in m) {
if (m[prop]?.toString && m[prop].toString().includes('.Marquee') && m[prop].toString().includes('--fade-length')) {
return m[prop];
}
}
return;
});

View File

@@ -1,6 +1,8 @@
import { FC, ReactNode } from 'react'; import { FC, ReactNode } from 'react';
import { fakeRenderComponent } from '../utils';
import { findModuleChild } from '../webpack'; import { findModuleChild } from '../webpack';
import { FooterLegendProps } from './FooterLegend';
export const showContextMenu: (children: ReactNode, parent?: EventTarget) => void = findModuleChild((m) => { export const showContextMenu: (children: ReactNode, parent?: EventTarget) => void = findModuleChild((m) => {
if (typeof m !== 'object') return undefined; if (typeof m !== 'object') return undefined;
@@ -11,10 +13,11 @@ export const showContextMenu: (children: ReactNode, parent?: EventTarget) => voi
} }
}); });
export interface MenuProps { export interface MenuProps extends FooterLegendProps {
label: string; label: string;
onCancel?(): void; onCancel?(): void;
cancelText?: string; cancelText?: string;
children?: ReactNode;
} }
export const Menu: FC<MenuProps> = findModuleChild((m) => { export const Menu: FC<MenuProps> = findModuleChild((m) => {
@@ -27,15 +30,47 @@ export const Menu: FC<MenuProps> = findModuleChild((m) => {
} }
}); });
export interface MenuItemProps { export interface MenuGroupProps {
onSelected?(): void; label: string;
disabled?: boolean;
children?: ReactNode;
}
export const MenuGroup: FC<MenuGroupProps> = findModuleChild((m) => {
if (typeof m !== 'object') return undefined;
for (let prop in m) {
if (
(m[prop]?.toString()?.includes('bInGamepadUI:') &&
fakeRenderComponent(() => m[prop]())?.type?.prototype?.RenderSubMenu) ||
(m[prop]?.prototype?.RenderSubMenu && m[prop]?.prototype?.ShowSubMenu)
) {
return m[prop];
}
}
});
export interface MenuItemProps extends FooterLegendProps {
bInteractableItem?: boolean;
onClick?(evt: Event): void;
onSelected?(evt: Event): void;
onMouseEnter?(evt: MouseEvent): void;
onMoveRight?(): void;
selected?: boolean;
disabled?: boolean;
bPlayAudio?: boolean;
tone?: 'positive' | 'emphasis' | 'destructive';
children?: ReactNode;
} }
export const MenuItem: FC<MenuItemProps> = findModuleChild((m) => { export const MenuItem: FC<MenuItemProps> = findModuleChild((m) => {
if (typeof m !== 'object') return undefined; if (typeof m !== 'object') return undefined;
for (let prop in m) { for (let prop in m) {
if (m[prop]?.prototype?.OnOKButton && m[prop]?.prototype?.OnMouseEnter) { if (
m[prop]?.render?.toString()?.includes('bPlayAudio:') ||
(m[prop]?.prototype?.OnOKButton && m[prop]?.prototype?.OnMouseEnter)
) {
return m[prop]; return m[prop];
} }
} }

View File

@@ -1,9 +1,60 @@
import { FC, ReactNode } from 'react'; import { FC, ReactNode } from 'react';
import { findModuleChild } from '../webpack'; import { findSP } from '../utils';
import { findModule, findModuleChild } from '../webpack';
// TODO: there is another argument, figure out what it does // All of the popout options + strTitle are related. Proper usage is not yet known...
export const showModal: (children: ReactNode, parent?: EventTarget) => void = findModuleChild((m) => { export interface ShowModalProps {
browserContext?: unknown; // This is another Deck Object that is yet to be found
bForcePopOut?: boolean;
bHideActionIcons?: boolean;
bHideMainWindowForPopouts?: boolean;
bNeverPopOut?: boolean;
fnOnClose?: () => void; // Seems to be the same as "closeModal" callback, but only when the modal is a popout. Will no longer work after "Update" invocation!
popupHeight?: number;
popupWidth?: number;
promiseRenderComplete?: Promise<void>; // Invoked once the render is complete. Currently, it seems to be used as image loading success/error callback...
strTitle?: string;
}
export interface ShowModalResult {
// This method will not invoke any of the variations of "closeModal" callbacks!
Close: () => void;
// This method will replace the modal element completely and will not update the callback chains,
// meaning that "closeModal" and etc. will not automatically close the modal anymore (also "fnOnClose"
// will not be even called upon close anymore)! You have to manually call the "Close" method when, for example,
// the "closeModal" is invoked in the newly updated modal:
// <ModalRoot closeModal={() => { console.log("ABOUT TO CLOSE"); showModalRes.Close(); }} />
Update: (modal: ReactNode) => void;
}
const showModalRaw:
| ((
modal: ReactNode,
parent?: EventTarget,
title?: string,
props?: ShowModalProps,
unknown1?: unknown,
hideActions?: { bHideActions?: boolean },
modalManager?: unknown,
) => Promise<ShowModalResult>)
| void = findModuleChild((m) => {
if (typeof m !== 'object') return undefined;
for (let prop in m) {
if (
typeof m[prop] === 'function' &&
m[prop].toString().includes('props.bDisableBackgroundDismiss') &&
!m[prop]?.prototype?.Cancel
) {
return m[prop];
}
}
});
const oldShowModalRaw:
| ((modal: ReactNode, parent?: EventTarget, props?: ShowModalProps) => Promise<ShowModalResult>)
| void = findModuleChild((m) => {
if (typeof m !== 'object') return undefined; if (typeof m !== 'object') return undefined;
for (let prop in m) { for (let prop in m) {
if (typeof m[prop] === 'function' && m[prop].toString().includes('bHideMainWindowForPopouts:!0')) { if (typeof m[prop] === 'function' && m[prop].toString().includes('bHideMainWindowForPopouts:!0')) {
@@ -12,12 +63,31 @@ export const showModal: (children: ReactNode, parent?: EventTarget) => void = fi
} }
}); });
export const showModal = (
modal: ReactNode,
parent?: EventTarget,
props: ShowModalProps = {
strTitle: 'Decky Dialog',
bHideMainWindowForPopouts: false,
},
): Promise<ShowModalResult> => {
if (showModalRaw) {
return showModalRaw(modal, parent || findSP(), props.strTitle, props, undefined, {
bHideActions: props.bHideActionIcons,
});
} else if (oldShowModalRaw) {
return oldShowModalRaw(modal, parent || findSP(), props);
} else {
throw new Error('[DFL:Modals]: Cannot find showModal function');
}
};
export interface ModalRootProps { export interface ModalRootProps {
onMiddleButton?(): void; children?: ReactNode;
onCancel?(): void; onCancel?(): void;
closeModal?(): void;
onOK?(): void; onOK?(): void;
onEscKeypress?(): void; onEscKeypress?(): void;
closeModal?(): void;
className?: string; className?: string;
modalClassName?: string; modalClassName?: string;
bAllowFullSize?: boolean; bAllowFullSize?: boolean;
@@ -25,13 +95,49 @@ export interface ModalRootProps {
bDisableBackgroundDismiss?: boolean; bDisableBackgroundDismiss?: boolean;
bHideCloseIcon?: boolean; bHideCloseIcon?: boolean;
bOKDisabled?: boolean; bOKDisabled?: boolean;
bCancelDisabled?: boolean;
} }
export const ModalRoot = findModuleChild((m) => { export interface ConfirmModalProps extends ModalRootProps {
onMiddleButton?(): void; // setting this prop will enable the middle button
strTitle?: ReactNode;
strDescription?: ReactNode;
strOKButtonText?: ReactNode;
strCancelButtonText?: ReactNode;
strMiddleButtonText?: ReactNode;
bAlertDialog?: boolean; // This will open a modal with only OK button enabled
bMiddleDisabled?: boolean;
}
export const ConfirmModal = findModuleChild((m) => {
if (typeof m !== 'object') return undefined; if (typeof m !== 'object') return undefined;
for (let prop in m) { for (let prop in m) {
if (!m[prop]?.prototype?.OK && m[prop]?.prototype?.Cancel && m[prop]?.prototype?.render) { if (!m[prop]?.prototype?.OK && m[prop]?.prototype?.Cancel && m[prop]?.prototype?.render) {
return m[prop]; return m[prop];
} }
} }
}) as FC<ModalRootProps>; }) as FC<ConfirmModalProps>;
// new
export const ModalRoot = (Object.values(
findModule((m: any) => {
if (typeof m !== 'object') return false;
for (let prop in m) {
if (m[prop]?.toString()?.includes('"ModalManager","DialogWrapper"')) {
return true;
}
}
return false;
}) || {},
)?.find((x: any) => x?.type?.toString()?.includes('((function(){')) ||
// old
findModuleChild((m) => {
if (typeof m !== 'object') return undefined;
for (let prop in m) {
if (m[prop]?.prototype?.OK && m[prop]?.prototype?.Cancel && m[prop]?.prototype?.render) {
return m[prop];
}
}
})) as FC<ModalRootProps>;

View File

@@ -1,4 +1,4 @@
import { VFC, ReactNode } from 'react'; import { ReactNode, VFC } from 'react';
import { findModuleChild } from '../webpack'; import { findModuleChild } from '../webpack';
import { ItemProps } from './Item'; import { ItemProps } from './Item';

View File

@@ -57,11 +57,11 @@ export enum DisplayStatus {
} }
export type AppOverview = { export type AppOverview = {
appid: string appid: string;
display_name: string display_name: string;
display_status: DisplayStatus display_status: DisplayStatus;
sort_as: string sort_as: string;
} };
export interface Router { export interface Router {
CloseSideMenus(): void; CloseSideMenus(): void;
@@ -69,14 +69,24 @@ export interface Router {
GetQuickAccessTab(): QuickAccessTab; GetQuickAccessTab(): QuickAccessTab;
Navigate(path: string): void; Navigate(path: string): void;
NavigateBackOrOpenMenu(): void; NavigateBackOrOpenMenu(): void;
NavigateToAppProperties(): void;
NavigateToBugForum(): void;
NavigateToExternalWeb(url: string): void; NavigateToExternalWeb(url: string): void;
NavigateToHelp(): void;
NavigateToInvites(): void;
NavigateToRunningApp(replace?: boolean): void; NavigateToRunningApp(replace?: boolean): void;
NavigateToStorage(): void;
NavigateToStore(): void; NavigateToStore(): void;
NavigateToStoreApp(appId: number | string): void;
NavigateToStoreFreeToPlay(): void;
NavigateToStoreManual(): void;
NavigateToStoreNewReleases(): void;
NavigateToStoreOnSale(): void;
ToggleSideMenu(sideMenu: SideMenu): void; ToggleSideMenu(sideMenu: SideMenu): void;
CloseSideMenus(): void; CloseSideMenus(): void;
OpenSideMenu(sideMenu: SideMenu): void; OpenSideMenu(sideMenu: SideMenu): void;
OpenPowerMenu(unknown?: any): void; OpenPowerMenu(unknown?: any): void;
get RunningApps(): any; get RunningApps(): AppOverview[];
get MainRunningApp(): AppOverview | undefined; get MainRunningApp(): AppOverview | undefined;
} }

View File

@@ -2,17 +2,25 @@ import { ReactNode, VFC } from 'react';
import { Module, findModuleChild } from '../webpack'; import { Module, findModuleChild } from '../webpack';
export interface SidebarNavigationPages { export interface SidebarNavigationPage {
title: string; title: string;
route: string;
content: ReactNode; content: ReactNode;
icon?: ReactNode;
visible?: boolean;
hideTitle?: boolean;
identifier?: string;
route?: string;
link?: string;
padding?: 'none' | 'compact';
} }
export interface SidebarNavigationProps { export interface SidebarNavigationProps {
title?: string; title?: string;
pages: SidebarNavigationPages[]; pages: SidebarNavigationPage[];
showTitle?: boolean; showTitle?: boolean;
disableRouteReporting?: boolean; disableRouteReporting?: boolean;
page?: string;
onPageRequested?: (page: string) => void;
} }
export const SidebarNavigation = findModuleChild((mod: Module) => { export const SidebarNavigation = findModuleChild((mod: Module) => {

View File

@@ -3,6 +3,6 @@ import { FC, SVGAttributes } from 'react';
import { IconsModule } from '../webpack'; import { IconsModule } from '../webpack';
// TODO type this and other icons? // TODO type this and other icons?
export const Spinner = Object.values(IconsModule).find((mod: any) => export const Spinner = Object.values(IconsModule).find(
mod?.toString && /Spinner\)}\),.\.createElement\(\"path\",{d:\"M18 /.test(mod.toString()) (mod: any) => mod?.toString && /Spinner\)}\),.\.createElement\(\"path\",{d:\"M18 /.test(mod.toString()),
) as FC<SVGAttributes<SVGElement>>; ) as FC<SVGAttributes<SVGElement>>;

View File

@@ -0,0 +1,300 @@
export interface Apps {
RegisterForAppOverviewChanges: any;
RegisterForAppDetails: any;
RegisterForLocalizationChanges: any;
RegisterForWorkshopChanges: any;
RegisterForWorkshopItemDownloads: any;
GetLibraryBootstrapData: any;
RegisterForAchievementChanges: any;
GetFriendAchievementsForApp: any;
GetMyAchievementsForApp: any;
AddUserTagToApps: any;
RemoveUserTagFromApps: any;
ClearUserTagsOnApps: any;
ClearAndSetUserTagsOnApp: any;
SetAppHidden: any;
ResetHiddenState: any;
SetAppLaunchOptions: any;
SetAppResolutionOverride: any;
SetAppCurrentLanguage: any;
SetAppAutoUpdateBehavior: any;
SetAppBackgroundDownloadsBehavior: any;
ToggleAppFamilyBlockedState: any;
ToggleAppSteamCloudEnabled: any;
ToggleAppSteamCloudSyncOnSuspendEnabled: any;
ToggleOverrideResolutionForInternalDisplay: any;
ToggleEnableSteamOverlayForApp: any;
ToggleEnableDesktopTheatreForApp: any;
BrowseLocalFilesForApp: any;
BrowseScreenshotsForApp: any;
BrowseScreenshotForApp: any;
BackupFilesForApp: any;
VerifyFilesForApp: any;
CreateDesktopShortcutForApp: any;
JoinAppContentBeta: any;
JoinAppContentBetaByPassword: any;
GetAchievementsInTimeRange: any;
GetSubscribedWorkshopItems: any;
SubscribeWorkshopItem: any;
GetDownloadedWorkshopItems: any;
DownloadWorkshopItem: any;
SetLocalScreenshotCaption: any;
SetLocalScreenshotSpoiler: any;
GetDetailsForScreenshotUpload: any;
UploadLocalScreenshot: any;
DeleteLocalScreenshot: any;
GetScreenshotsInTimeRange: any;
GetFriendsWhoPlay: any;
RequestLegacyCDKeysForApp: any;
GetSoundtrackDetails: any;
GetStoreTagLocalization: any;
GetLaunchOptionsForApp: any;
GetResolutionOverrideForApp: any;
ScanForShortcuts: any;
GetAllShortcuts: any;
GetShortcutData: any;
AddShortcut: any;
RemoveShortcut: any;
InstallFlatpakAppAndCreateShortcut: any;
ListFlatpakApps: any;
UninstallFlatpakApp: any;
ShowControllerConfigurator: any;
SetThirdPartyControllerConfiguration: any;
ToggleAllowDesktopConfiguration: any;
SetControllerRumblePreference: any;
GetCachedAppDetails: any;
SetCachedAppDetails: any;
ReportLibraryAssetCacheMiss: any;
SaveAchievementProgressCache: any;
SetStreamingClientForApp: any;
SetCustomArtworkForApp: any;
ClearCustomArtworkForApp: any;
SetCustomLogoPositionForApp: any;
ClearCustomLogoPositionForApp: any;
RequestIconDataForApp: any;
SpecifyCompatTool: any;
GetAvailableCompatTools: any;
SetShortcutName: any;
SetShortcutExe: any;
SetShortcutStartDir: any;
SetShortcutLaunchOptions: any;
SetShortcutIsVR: any;
PromptToChangeShortcut: any;
PromptToSelectShortcutIcon: any;
InstallApp: any;
RunGame: any;
VerifyApp: any;
StreamGame: any;
CancelLaunch: any;
TerminateApp: any;
UninstallApps: any;
ShowStore: any;
SetDLCEnabled: any;
ContinueGameAction: any;
CancelGameAction: any;
GetActiveGameActions: any;
GetGameActionDetails: any;
GetGameActionForApp: any;
SkipShaderProcessing: any;
MarkEulaAccepted: any;
MarkEulaRejected: any;
LoadEula: any;
GetConflictingFileTimestamps: any;
GetCloudPendingRemoteOperations: any;
ClearProton: any;
RegisterForMarketingMessages: any;
FetchMarketingMessages: any;
MarkMarketingMessageSeen: any;
ReportMarketingMessageSeen: any;
RegisterForGameActionStart: any;
RegisterForGameActionEnd: any;
RegisterForGameActionTaskChange: any;
RegisterForGameActionUserRequest: any;
RegisterForGameActionShowError: any;
RegisterForGameActionShowUI: any;
OpenAppSettingsDialog: any;
}
export interface Window {
RegisterForExternalDisplayChanged: any;
SetManualDisplayScaleFactor: any;
SetAutoDisplayScale: any;
Minimize: any;
ProcessShuttingDown: any;
ToggleMaximize: any;
MoveTo: any;
ResizeTo: any;
SetMinSize: any;
SetResizeGrip: any;
SetComposition: any;
GamescopeBlur: any;
BringToFront: any;
SetForegroundWindow: any;
SetKeyFocus: any;
FlashWindow: any;
StopFlashWindow: any;
ShowWindow: any;
HideWindow: any;
SetWindowIcon: any;
GetWindowDimensions: any;
GetWindowRestoreDetails: any;
PositionWindowRelative: any;
GetMousePositionDetails: any;
IsWindowMinimized: any;
GetBrowserID: any;
}
export interface SteamClient {
Apps: Apps;
Browser: any;
BrowserView: any;
ClientNotifications: any;
Cloud: any;
Console: any;
Downloads: any;
FamilySharing: any;
FriendSettings: any;
Friends: any;
GameSessions: any;
Input: any;
InstallFolder: any;
Installs: any;
MachineStorage: any;
Messaging: any;
Notifications: any;
OpenVR: any;
Overlay: any;
Parental: any;
RegisterIFrameNavigatedCallback: any;
RemotePlay: any;
RoamingStorage: any;
Screenshots: any;
Settings: any;
SharedConnection: any;
Stats: any;
Storage: any;
Streaming: any;
System: any;
UI: any;
URL: any;
Updates: any;
User: any;
WebChat: any;
Window: Window;
}
export interface SteamShortcut {
appid: number;
data: {
bIsApplication: boolean;
strAppName: string;
strExePath: string;
strArguments: string;
strShortcutPath: string;
strSortAs: string;
};
}
/**
* @prop unAppID is not properly set by Steam for non-steam game shortcuts, so it defaults to 0 for them
*/
export interface LifetimeNotification {
unAppID: number;
nInstanceID: number;
bRunning: boolean;
}
export type AppAchievements = {
nAchieved: number;
nTotal: number;
vecAchievedHidden: any[];
vecHighlight: any[];
vecUnachieved: any[];
};
export type AppLanguages = {
strDisplayName: string;
strShortName: string;
};
export interface AppDetails {
achievements: AppAchievements;
bCanMoveInstallFolder: boolean;
bCloudAvailable: boolean;
bCloudEnabledForAccount: boolean;
bCloudEnabledForApp: boolean;
bCloudSyncOnSuspendAvailable: boolean;
bCloudSyncOnSuspendEnabled: boolean;
bCommunityMarketPresence: boolean;
bEnableAllowDesktopConfiguration: boolean;
bFreeRemovableLicense: boolean;
bHasAllLegacyCDKeys: boolean;
bHasAnyLocalContent: boolean;
bHasLockedPrivateBetas: boolean;
bIsExcludedFromSharing: boolean;
bIsSubscribedTo: boolean;
bOverlayEnabled: boolean;
bOverrideInternalResolution: boolean;
bRequiresLegacyCDKey: boolean;
bShortcutIsVR: boolean;
bShowCDKeyInMenus: boolean;
bShowControllerConfig: boolean;
bSupportsCDKeyCopyToClipboard: boolean;
bVRGameTheatreEnabled: boolean;
bWorkshopVisible: boolean;
eAppOwnershipFlags: number;
eAutoUpdateValue: number;
eBackgroundDownloads: number;
eCloudSync: number;
eControllerRumblePreference: number;
eDisplayStatus: number;
eEnableThirdPartyControllerConfiguration: number;
eSteamInputControllerMask: number;
iInstallFolder: number;
lDiskUsageBytes: number;
lDlcUsageBytes: number;
nBuildID: number;
nCompatToolPriority: number;
nPlaytimeForever: number;
nScreenshots: number;
rtLastTimePlayed: number;
rtLastUpdated: number;
rtPurchased: number;
selectedLanguage: {
strDisplayName: string;
strShortName: string;
};
strCloudBytesAvailable: string;
strCloudBytesUsed: string;
strCompatToolDisplayName: string;
strCompatToolName: string;
strDeveloperName: string;
strDeveloperURL: string;
strDisplayName: string;
strExternalSubscriptionURL: string;
strFlatpakAppID: string;
strHomepageURL: string;
strLaunchOptions: string;
strManualURL: string;
strOwnerSteamID: string;
strResolutionOverride: string;
strSelectedBeta: string;
strShortcutExe: string;
strShortcutLaunchOptions: string;
strShortcutStartDir: string;
strSteamDeckBlogURL: string;
unAppID: number;
vecBetas: any[];
vecDLC: any[];
vecDeckCompatTestResults: any[];
vecLanguages: AppLanguages[];
vecLegacyCDKeys: any[];
vecMusicAlbums: any[];
vecPlatforms: string[];
vecScreenShots: any[];
}
export interface SteamAppOverview {
display_name: string;
gameid: string;
}

View File

@@ -1,9 +1,11 @@
import { FC, SVGAttributes } from 'react'; import { FC, SVGAttributes } from 'react';
import { findModuleChild } from '../webpack'; import { findModuleChild } from '../webpack';
export const SteamSpinner = findModuleChild((m) => { export const SteamSpinner = findModuleChild((m) => {
if (typeof m !== "object") return undefined; if (typeof m !== 'object') return undefined;
for (let prop in m) { for (let prop in m) {
if (m[prop]?.toString()?.includes("Steam Spinner") && m[prop].toString().includes("PreloadThrobber")) return m[prop] if (m[prop]?.toString()?.includes('Steam Spinner') && m[prop].toString().includes('PreloadThrobber'))
} return m[prop];
}) as FC<SVGAttributes<SVGElement>>; }
}) as FC<SVGAttributes<SVGElement>>;

View File

@@ -0,0 +1,134 @@
import { FC, ReactNode, createElement, useEffect, useState } from 'react';
import { fakeRenderComponent, findInReactTree, sleep } from '../utils';
import { findModule } from '../webpack';
import { FooterLegendProps } from './FooterLegend';
import { SteamSpinner } from './SteamSpinner';
/**
* Individual tab objects for the Tabs component
*
* `id` ID of this tab, can be used with activeTab to auto-focus a given tab
* `title` Title shown in the header bar
* `renderTabAddon` Return a {@link ReactNode} to render it next to the tab title, i.e. the counts for each tab on the Media page
* `content` Content of the tab
* `footer` Sets up button handlers and labels
*/
export interface Tab {
id: string;
title: string;
renderTabAddon?: () => ReactNode;
content: ReactNode;
footer?: FooterLegendProps;
}
/**
* Props for the {@link Tabs}
*
* `tabs` array of {@link Tab}
* `activeTab` tab currently active, needs to be one of the tabs {@link Tab.id}, must be set using a `useState` in the `onShowTab` handler
* `onShowTab` Called when the active tab should change, needs to set `activeTab`. See example.
* `autoFocusContents` Whether to automatically focus the tab contents or not.
* `footer` Sets up button handlers and labels
*
* @example
* const Component: FC = () => {
* const [currentTab, setCurrentTab] = useState<string>("Tab1");
*
* return (
* <Tabs
* title="Theme Manager"
* activeTab={currentTabRoute}
* onShowTab={(tabID: string) => {
* setCurrentTabRoute(tabID);
* }}
* tabs={[
* {
* title: "Tab 1",
* content: <Tab1Component />,
* id: "Tab1",
* },
* {
* title: "Tab 2",
* content: <Tab2Component />,
* id: "Tab2",
* },
* ]}
* />
* );
* };
*/
export interface TabsProps {
tabs: Tab[];
activeTab: string;
onShowTab: (tab: string) => void;
autoFocusContents?: boolean;
}
let tabsComponent: any;
const getTabs = async () => {
if (tabsComponent) return tabsComponent;
// @ts-ignore
while (!window?.DeckyPluginLoader?.routerHook?.routes) {
console.debug('[DFL:Tabs]: Waiting for Decky router...');
await sleep(500);
}
return (tabsComponent = fakeRenderComponent(
() => {
return findInReactTree(
findInReactTree(
// @ts-ignore
window.DeckyPluginLoader.routerHook.routes
.find((x: any) => x.props.path == '/library/app/:appid/achievements')
.props.children.type(),
(x) => x?.props?.scrollTabsTop,
).type({ appid: 1 }),
(x) => x?.props?.tabs,
).type;
},
{
useRef: () => ({ current: { reaction: { track: () => {} } } }),
useContext: () => ({ match: { params: { appid: 1 } } }),
useMemo: () => ({ data: {} }),
},
));
};
let oldTabs: any;
try {
const oldTabsModule = findModule((m: any) => {
if (typeof m !== 'object') return false;
for (let prop in m) {
if (m[prop]?.Unbleed) return true;
}
return false;
});
if (oldTabsModule)
oldTabs = Object.values(oldTabsModule).find((x: any) => x?.type?.toString()?.includes('((function(){'));
} catch (e) {
console.error('Error finding oldTabs:', e);
}
/**
* Tabs component as used in the library and media tabs. See {@link TabsProps}
* Unlike other components in `decky-frontend-lib`, this requires Decky Loader to be running.
*/
export const Tabs =
oldTabs ||
(((props: TabsProps) => {
const found = tabsComponent;
const [tc, setTC] = useState<FC<TabsProps>>(found);
useEffect(() => {
if (found) return;
(async () => {
console.debug('[DFL:Tabs]: Finding component...');
const t = await getTabs();
console.debug('[DFL:Tabs]: Found!');
setTC(t);
})();
}, []);
console.log('tc', tc);
return tc ? createElement(tc, props) : <SteamSpinner />;
}) as FC<TabsProps>);

View File

@@ -1,11 +1,12 @@
import { ChangeEventHandler, ReactNode, VFC } from 'react'; import { ChangeEventHandler, HTMLAttributes, ReactNode, VFC } from 'react';
import { CommonUIModule, Module } from '../webpack'; import { CommonUIModule, Module } from '../webpack';
export interface TextFieldProps { export interface TextFieldProps extends HTMLAttributes<HTMLInputElement> {
label?: ReactNode; label?: ReactNode;
requiredLabel?: ReactNode; requiredLabel?: ReactNode;
description?: ReactNode; description?: ReactNode;
disabled?: boolean;
bShowCopyAction?: boolean; bShowCopyAction?: boolean;
bShowClearAction?: boolean; bShowClearAction?: boolean;
bAlwaysShowClearAction?: boolean; bAlwaysShowClearAction?: boolean;

View File

@@ -1,8 +1,15 @@
export * from './Button'; export * from './Button';
export * from './ButtonItem'; export * from './ButtonItem';
export * from './Carousel';
export * from './ControlsList';
export * from './Dialog';
export * from './DialogCheckbox';
export * from './Dropdown'; export * from './Dropdown';
export * from './Field'; export * from './Field';
export * from './Focusable'; export * from './Focusable';
export * from './FocusRing';
export * from './FooterLegend';
export * from './Marquee';
export * from './Menu'; export * from './Menu';
export * from './Modal'; export * from './Modal';
export * from './Panel'; export * from './Panel';
@@ -13,6 +20,14 @@ export * from './SliderField';
export * from './Spinner'; export * from './Spinner';
export * from './static-classes'; export * from './static-classes';
export * from './SteamSpinner'; export * from './SteamSpinner';
export * from './Tabs';
export * from './TextField'; export * from './TextField';
export * from './Toggle'; export * from './Toggle';
export * from './ToggleField'; export * from './ToggleField';
export * from './SteamClient';
import {SteamClient} from './SteamClient'
declare global {
var SteamClient: SteamClient;
}

View File

@@ -1,6 +1,6 @@
import { findModule } from '../webpack'; import { findModule } from '../webpack';
type StaticClasses = Record< type QuickAccessMenuClasses = Record<
| 'ActiveTab' | 'ActiveTab'
| 'AllTabContents' | 'AllTabContents'
| 'BatteryDetailsLabels' | 'BatteryDetailsLabels'
@@ -63,6 +63,8 @@ type StaticClasses = Record<
string string
>; >;
type ScrollPanelClasses = Record<'ScrollBoth' | 'ScrollPanel' | 'ScrollX' | 'ScrollY', string>;
type GamepadDialogClasses = Record< type GamepadDialogClasses = Record<
| 'duration-app-launch' | 'duration-app-launch'
| 'GamepadDialogContent' | 'GamepadDialogContent'
@@ -157,32 +159,320 @@ type QuickAccessControlsClasses = Record<
string string
>; >;
export const staticClasses: StaticClasses = findModule((mod) => { type UpdaterFieldClasses = Record<
if (typeof mod !== 'object') return false; | '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) { type PlaySectionClasses = Record<
return true; | '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; type GamepadSliderClasses = Record<
}); | 'error-shake-duration'
| 'SliderControlPanelGroup'
| 'SliderControlAndNotches'
| 'WithDefaultValue'
| 'SliderControl'
| 'Disabled'
| 'SliderTrack'
| 'SliderHasNotches'
| 'SliderTrackDark'
| 'SliderHandleContainer'
| 'VerticalLineSliderHandleContainer'
| 'ParenSliderHandleContainer'
| 'SliderHandle'
| 'SliderHandleFocusPop'
| 'VerticalLineSliderHandle'
| 'ParenSliderHandle'
| 'Left'
| 'SliderControlWithIcon'
| 'Icon'
| 'SliderNotchContainer'
| 'SliderNotch'
| 'AlignToEnds'
| 'SliderNotchLabel'
| 'AlignToLeft'
| 'AlignToRight'
| 'SliderNotchTick'
| 'TickActive'
| 'LabelText'
| 'DescriptionValue'
| 'EditableValue'
| 'FakeEditableValue'
| 'RedBorder'
| 'EditableValueSuffix'
| 'ErrorShake'
| 'error-shake'
| 'CompoundSlider'
| 'CompoundSliderSubSlider'
| 'Right'
| 'CompoundSliderSubSliderLabelContainer'
| 'CompoundSliderSubSliderLabelPositioner'
| 'CompoundSliderSubSliderLabel'
| 'CompoundSliderSubSliderLabelInternal'
| 'DefaultValueTickContainer'
| 'DefaultValueTick',
string
>;
export const gamepadDialogClasses: GamepadDialogClasses = findModule((mod) => { type AppDetailsHeaderClasses = Record<
if (typeof mod !== 'object') return false; | 'AddBoxSizer'
| 'Background'
| 'Bottom'
| 'BottomCenter'
| 'BottomLeft'
| 'BottomRight'
| 'BoxSizer'
| 'BoxSizerButtonContainer'
| 'BoxSizerContainer'
| 'BoxSizerDelete'
| 'BoxSizerDragBox'
| 'BoxSizerEdge'
| 'BoxSizerGridBox'
| 'BoxSizerInfo'
| 'BoxSizerSettings'
| 'BoxSizerValidRegion'
| 'CenterCenter'
| 'DialogButton'
| 'EdgeDown'
| 'FallbackArt'
| 'Features'
| 'FullscreenEnterActive'
| 'FullscreenEnterDone'
| 'FullscreenEnterStart'
| 'FullscreenExitActive'
| 'FullscreenExitDone'
| 'FullscreenExitStart'
| 'HeaderBackgroundImage'
| 'ImgBlur'
| 'ImgBlurBackdrop'
| 'ImgContainer'
| 'ImgSrc'
| 'Left'
| 'Loaded'
| 'Middle'
| 'NoArt'
| 'PinBox'
| 'Right'
| 'SVGTitle'
| 'SaveBoxSizer'
| 'TextNameSpace'
| 'TitleImageContainer'
| 'TitleLogo'
| 'TitleSection'
| 'Top'
| 'TopCapsule'
| 'TopGradient'
| 'TopLeft'
| 'TopRight'
| 'UpperCenter'
| 'UpperLeft'
| 'duration-app-launch',
string
>;
if (mod.WithFirstRow) { type AppDetailsClasses = Record<
return true; | 'BreakNarrow'
} | 'BreakShort'
| 'BreakTall'
| 'BreakUltraWide'
| 'BreakWide'
| 'Container'
| 'GamepadUIBreakNarrow'
| 'GamepadUIBreakShort'
| 'GamepadUIBreakWide'
| 'Glassy'
| 'Header'
| 'HeaderLoaded'
| 'InnerContainer'
| 'ItemFocusAnim-darkGrey'
| 'ItemFocusAnim-darkerGrey'
| 'ItemFocusAnim-darkerGrey-nocolor'
| 'ItemFocusAnim-green'
| 'ItemFocusAnim-grey'
| 'ItemFocusAnimBorder-darkGrey'
| 'PlayBar'
| 'PreventScrolling'
| 'RightBreakNarrow'
| 'RightBreakUltraNarrow'
| 'RightBreakUltraWide'
| 'RightBreakWide'
| 'ScrollContainer'
| 'ShowPlayBar'
| 'Throbber'
| 'duration-app-launch'
| 'fadein'
| 'focusAnimation'
| 'hoverAnimation',
string
>;
return false; export const quickAccessMenuClasses: QuickAccessMenuClasses = findModule(
}); (mod) => typeof mod === 'object' && mod?.Title?.includes('quickaccessmenu'),
);
export const quickAccessControlsClasses: QuickAccessControlsClasses = findModule((mod) => { /**
if (typeof mod !== 'object') return false; * @depreciated please use quickAccessMenuClasses instead
*/
if (mod.PanelSectionRow) { export const staticClasses = quickAccessMenuClasses;
return true; export const scrollPanelClasses: ScrollPanelClasses = findModule(
} (mod) => typeof mod === 'object' && mod?.ScrollPanel?.includes('scrollpanel'),
);
return false; /**
}); * @depreciated please use scrollPanelClasses instead
*/
export const scrollClasses = scrollPanelClasses;
export const gamepadDialogClasses: GamepadDialogClasses = findModule(
(mod) => typeof mod === 'object' && mod?.GamepadDialogContent?.includes('gamepaddialog'),
);
export const quickAccessControlsClasses: QuickAccessControlsClasses = findModule(
(mod) => typeof mod === 'object' && mod?.PanelSection?.includes('quickaccesscontrols'),
);
export const updaterFieldClasses: UpdaterFieldClasses = findModule(
(mod) => typeof mod === 'object' && mod?.OOBEUpdateStatusContainer?.includes('updaterfield'),
);
export const playSectionClasses: PlaySectionClasses = findModule(
(mod) => typeof mod === 'object' && mod?.Container?.includes('appdetailsplaysection'),
);
export const gamepadSliderClasses: GamepadSliderClasses = findModule(
(mod) => typeof mod === 'object' && mod?.SliderControlPanelGroup?.includes('gamepadslider'),
);
export const appDetailsHeaderClasses: AppDetailsHeaderClasses = findModule(
(mod) => typeof mod === 'object' && mod?.TopCapsule?.includes('sharedappdetailsheader'),
);
export const appDetailsClasses: AppDetailsClasses = findModule(
(mod) => typeof mod === 'object' && mod?.HeaderLoaded?.includes('appdetails_'),
);

1
src/deck-hooks/index.ts Normal file
View File

@@ -0,0 +1 @@
export * from './useParams';

View File

@@ -0,0 +1,15 @@
import { ReactRouter } from '../webpack';
/**
* Get the current params from ReactRouter
*
* @returns an object with the current ReactRouter params
*
* @example
* import { useParams } from "decky-frontend-lib";
*
* const { appid } = useParams<{ appid: string }>()
*/
export const useParams = Object.values(ReactRouter).find((val) => /return (\w)\?\1\.params:{}/.test(`${val}`)) as <
T,
>() => T;

View File

@@ -1,6 +1,8 @@
// export * from './deck-libs'; // export * from './deck-libs';
export * from './custom-components'; export * from './custom-components';
export * from './custom-hooks';
export * from './deck-components'; export * from './deck-components';
export * from './deck-hooks';
export * from './plugin'; export * from './plugin';
export * from './webpack'; export * from './webpack';
export * from './utils'; export * from './utils';

View File

@@ -1,4 +1,4 @@
import type { ComponentType } from 'react'; import type { ComponentType, ReactNode } from 'react';
import { RouteProps } from 'react-router'; import { RouteProps } from 'react-router';
export interface Plugin { export interface Plugin {
@@ -6,6 +6,7 @@ export interface Plugin {
icon: JSX.Element; icon: JSX.Element;
content?: JSX.Element; content?: JSX.Element;
onDismount?(): void; onDismount?(): void;
alwaysRender?: boolean;
} }
interface ServerResponseSuccess<TRes> { interface ServerResponseSuccess<TRes> {
@@ -18,19 +19,48 @@ interface ServerResponseError {
result: string; result: string;
} }
type ServerResponse<TRes> = ServerResponseSuccess<TRes> | ServerResponseError; export type ServerResponse<TRes> = ServerResponseSuccess<TRes> | ServerResponseError;
type RoutePatch = (route: RouteProps) => RouteProps; export type RoutePatch = (route: RouteProps) => RouteProps;
interface RouterHook { export interface RouterHook {
addRoute(path: string, component: ComponentType, props?: Omit<RouteProps, 'path' | 'children'>): void; addRoute(path: string, component: ComponentType, props?: Omit<RouteProps, 'path' | 'children'>): void;
addPatch(path: string, patch: RoutePatch): RoutePatch; addPatch(path: string, patch: RoutePatch): RoutePatch;
removePatch(path: string, patch: RoutePatch): void; addGlobalComponent(name: string, component: ComponentType): void;
removeRoute(path: string): void; removeRoute(path: string): void;
removePatch(path: string, patch: RoutePatch): void;
removeGlobalComponent(name: string): void;
}
export interface ToastData {
title: ReactNode;
body: ReactNode;
onClick?: () => void;
logo?: ReactNode;
icon?: ReactNode;
className?: string;
contentClassName?: string;
duration?: number;
critical?: boolean;
eType?: number;
sound?: number;
playSound?: boolean;
showToast?: boolean;
}
export interface Toaster {
toast(toast: ToastData): void;
}
export interface FilePickerRes {
path: string;
realpath: string;
} }
export interface ServerAPI { export interface ServerAPI {
routerHook: RouterHook; routerHook: RouterHook;
toaster: Toaster;
openFilePicker(startPath: string, includeFiles?: boolean, regex?: RegExp): Promise<FilePickerRes>;
callPluginMethod<TArgs = {}, TRes = {}>(methodName: string, args: TArgs): Promise<ServerResponse<TRes>>; callPluginMethod<TArgs = {}, TRes = {}>(methodName: string, args: TArgs): Promise<ServerResponse<TRes>>;
callServerMethod<TArgs = {}, TRes = {}>(methodName: string, args: TArgs): Promise<ServerResponse<TRes>>; callServerMethod<TArgs = {}, TRes = {}>(methodName: string, args: TArgs): Promise<ServerResponse<TRes>>;
fetchNoCors<TRes = {}>(url: RequestInfo, request?: RequestInit): Promise<ServerResponse<TRes>>; fetchNoCors<TRes = {}>(url: RequestInfo, request?: RequestInit): Promise<ServerResponse<TRes>>;

View File

@@ -1,95 +0,0 @@
import * as React from "react";
// this shouldn't need to be redeclared but it does for some reason
declare global {
interface Window {
SP_REACT: typeof React;
}
}
export function fakeRenderComponent(fun: Function): any {
const hooks = (window.SP_REACT as any).__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentDispatcher.current;
// TODO: add more hooks
let oldHooks = {
useContext: hooks.useContext,
useCallback: hooks.useCallback,
useLayoutEffect: hooks.useLayoutEffect,
useEffect: hooks.useEffect,
useMemo: hooks.useMemo,
useRef: hooks.useRef,
useState: hooks.useState,
}
hooks.useCallback = (cb: Function) => cb;
hooks.useContext = (cb: any) => cb._currentValue;
hooks.useLayoutEffect = (_: Function) => {}//cb();
hooks.useMemo = (cb: Function, _: any[]) => cb;
hooks.useEffect = (_: Function) => {}//cb();
hooks.useRef = (val: any) => ({current: val || {}});
hooks.useState = (v: any) => {
let val = v;
return [val, (n: any) => val = n];
};
const res = fun(hooks);
Object.assign(hooks, oldHooks);
return res;
}
export function beforePatch(obj: any, name: string, fnc: (args: any[]) => any): 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);
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): 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);
return ret;
};
Object.assign(obj[name], orig);
obj[name].toString = () => orig.toString();
obj[name].__deckyOrig = orig;
}
// 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;
}
export function getReactInstance(o: HTMLElement | Element | Node) {
return o[Object.keys(o).find(k => k.startsWith('__reactInternalInstance')) as string]
}
export function joinClassNames(...classes: string[]): string {
return classes.join(" ");
}
export function sleep(ms: number) {
return new Promise(res => setTimeout(res, ms));
}

22
src/utils/index.ts Normal file
View File

@@ -0,0 +1,22 @@
export * from './patcher';
export * from './react';
export function joinClassNames(...classes: string[]): string {
return classes.join(' ');
}
export function sleep(ms: number) {
return new Promise((res) => setTimeout(res, ms));
}
/**
* Finds the SP window, since it is a render target as of 10-19-2022's beta
*/
export function findSP(): Window {
// old (SP as host)
if (document.title == 'SP') return window;
// new (SP as popup)
const context = FocusNavController.m_ActiveContext || FocusNavController.m_LastActiveContext;
return context.m_rgGamepadNavigationTrees.find((x: any) => x.m_ID == 'root_1_').Root
.Element.ownerDocument.defaultView;
}

160
src/utils/patcher.ts Normal file
View File

@@ -0,0 +1,160 @@
// TODO: implement storing patches as an option so we can offer unpatchAll selectively
// Return this in a replacePatch to call the original method (can still modify args).
export let callOriginal = Symbol('DECKY_CALL_ORIGINAL');
export interface PatchOptions {
singleShot?: boolean;
}
type GenericPatchHandler = (args: any[], ret?: any) => any;
export interface Patch {
original: Function;
property: string;
object: any;
patchedFunction: any;
hasUnpatched: boolean;
handler: GenericPatchHandler;
unpatch: () => void;
}
// let patches = new Set<Patch>();
export function beforePatch(
object: any,
property: string,
handler: (args: any[]) => any,
options: PatchOptions = {},
): Patch {
const orig = object[property];
object[property] = function (...args: any[]) {
handler.call(this, args);
const ret = patch.original.call(this, ...args);
if (options.singleShot) {
patch.unpatch();
}
return ret;
};
const patch = processPatch(object, property, handler, object[property], orig);
return patch;
}
export function afterPatch(
object: any,
property: string,
handler: (args: any[], ret: any) => any,
options: PatchOptions = {},
): Patch {
const orig = object[property];
object[property] = function (...args: any[]) {
let ret = patch.original.call(this, ...args);
ret = handler.call(this, args, ret);
if (options.singleShot) {
patch.unpatch();
}
return ret;
};
const patch = processPatch(object, property, handler, object[property], orig);
return patch;
}
export function replacePatch(
object: any,
property: string,
handler: (args: any[]) => any,
options: PatchOptions = {},
): Patch {
const orig = object[property];
object[property] = function (...args: any[]) {
const ret = handler.call(this, args);
if (ret == callOriginal) return patch.original.call(this, ...args);
if (options.singleShot) {
patch.unpatch();
}
return ret;
};
const patch = processPatch(object, property, handler, object[property], orig);
return patch;
}
function processPatch(
object: any,
property: any,
handler: GenericPatchHandler,
patchedFunction: any,
original: any,
): Patch {
// Assign all props of original function to new one
Object.assign(object[property], original);
// Allow toString webpack filters to continue to work
object[property].toString = () => original.toString();
// HACK: for compatibility, remove when all plugins are using new patcher
Object.defineProperty(object[property], '__deckyOrig', {
get: () => patch.original,
set: (val: any) => (patch.original = val),
});
// Build a Patch object of this patch
const patch: Patch = {
object,
property,
handler,
patchedFunction,
original,
hasUnpatched: false,
unpatch: () => unpatch(patch),
};
object[property].__deckyPatch = patch;
return patch;
}
function unpatch(patch: Patch): void {
const { object, property, handler, patchedFunction, original } = patch;
if (patch.hasUnpatched) throw new Error('Function is already unpatched.');
let realProp = property;
let realObject = object;
console.debug('[Patcher] unpatching', {
realObject,
realProp,
object,
property,
handler,
patchedFunction,
original,
isEqual: realObject[realProp] === patchedFunction,
});
// If another patch has been applied to this function after this one, move down until we find the correct patch
while (realObject[realProp] && realObject[realProp] !== patchedFunction) {
realObject = realObject[realProp].__deckyPatch;
realProp = 'original';
console.debug('[Patcher] moved to next', {
realObject,
realProp,
object,
property,
handler,
patchedFunction,
original,
isEqual: realObject[realProp] === patchedFunction,
});
}
realObject[realProp] = realObject[realProp].__deckyPatch.original;
patch.hasUnpatched = true;
console.debug('[Patcher] unpatched', {
realObject,
realProp,
object,
property,
handler,
patchedFunction,
original,
isEqual: realObject[realProp] === patchedFunction,
});
}

95
src/utils/react.ts Normal file
View File

@@ -0,0 +1,95 @@
import * as React from 'react';
// this shouldn't need to be redeclared but it does for some reason
declare global {
interface Window {
SP_REACT: typeof React;
}
}
export function fakeRenderComponent(fun: Function, customHooks: any = {}): any {
const hooks = (window.SP_REACT as any).__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentDispatcher
.current;
// TODO: add more hooks
let oldHooks = {
useContext: hooks.useContext,
useCallback: hooks.useCallback,
useLayoutEffect: hooks.useLayoutEffect,
useEffect: hooks.useEffect,
useMemo: hooks.useMemo,
useRef: hooks.useRef,
useState: hooks.useState,
};
hooks.useCallback = (cb: Function) => cb;
hooks.useContext = (cb: any) => cb._currentValue;
hooks.useLayoutEffect = (_: Function) => {}; //cb();
hooks.useMemo = (cb: Function, _: any[]) => cb;
hooks.useEffect = (_: Function) => {}; //cb();
hooks.useRef = (val: any) => ({ current: val || {} });
hooks.useState = (v: any) => {
let val = v;
return [val, (n: any) => (val = n)];
};
Object.assign(hooks, customHooks);
const res = fun(hooks);
Object.assign(hooks, oldHooks);
return res;
}
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) {
return o[Object.keys(o).find((k) => k.startsWith('__reactInternalInstance')) as string];
}
// 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 { declare global {
interface Window { interface Window {
webpackJsonp: any; webpackJsonp: any;
webpackChunksteamui: any;
} }
} }
@@ -9,15 +10,42 @@ export type Module = any;
type FilterFn = (module: any) => boolean; type FilterFn = (module: any) => boolean;
type FindFn = (module: any) => any; type FindFn = (module: any) => any;
const wpRequire = window.webpackJsonp.push([ export let webpackCache: any = {};
[], let hasWebpack5 = false;
{ get_require: (mod: any, _exports: any, wpRequire: any) => (mod.exports = wpRequire) },
[['get_require']],
]);
export const allModules: Module[] = Object.keys(wpRequire.c) if (window.webpackJsonp && !window.webpackJsonp.deckyShimmed) {
.map((x) => wpRequire.c[x].exports) // Webpack 4, currently on stable
.filter((x) => x); 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) => { export const findModule = (filter: FilterFn) => {
for (const m of allModules) { for (const m of allModules) {

5
typedoc.json Normal file
View File

@@ -0,0 +1,5 @@
{
"githubPages": false,
"categorizeByGroup": false,
"excludeExternals": true
}