Compare commits

...

152 Commits

Author SHA1 Message Date
semantic-release-bot
b151bdce8b chore(release): 4.11.4 [CI SKIP] 2026-05-15 03:58:28 +00:00
AAGaming
e44664c970 fix(dropdown): hack to fix styling in dropdownitem 2026-05-14 23:57:56 -04:00
AAGaming
ac8da8e9b6 fix(Field): fix filter for field on beta 2026-05-14 23:57:06 -04:00
semantic-release-bot
b688bc3544 chore(release): 4.11.3 [CI SKIP] 2026-03-22 00:34:53 +00:00
Jozen Blue Martinez
3126dd3e04 fix(Tabs): update for latest beta (#129) 2026-03-21 20:34:27 -04:00
semantic-release-bot
8596294667 chore(release): 4.11.2 [CI SKIP] 2026-03-21 01:29:42 +00:00
AAGaming
772a85523b fix(ci): update semantic release 2026-03-20 21:29:12 -04:00
AAGaming
aabc522740 fix(ci): microsoft 2026-03-20 21:26:44 -04:00
AAGaming
66e0afccae fix(ci): microsoft stinks 2026-03-20 21:23:44 -04:00
AAGaming
77e6acd828 fix(ci): use new npm publishing auth 2026-03-20 21:22:23 -04:00
AAGaming
261162c8bc fix(components): update for latest beta 2026-03-20 20:27:59 -04:00
semantic-release-bot
f8fda380f1 chore(release): 4.11.1 [CI SKIP] 2025-11-27 02:56:17 +00:00
AAGaming
7242c69758 fix(webpack): ignore window module (lol), ignore filter errors 2025-11-26 21:55:32 -05:00
semantic-release-bot
fd651eb989 chore(release): 4.11.0 [CI SKIP] 2025-10-15 04:05:49 +00:00
AAGaming
6e443c06d3 feat(*): react 19 support update 2025-10-15 00:04:31 -04:00
ynhhoJ
25b4b60e34 types(input): Add GamepadButton enum & set correct type for RegisterForControllerInputMessage (#125) 2025-09-22 10:13:29 -04:00
semantic-release-bot
cf6572cd7b chore(release): 4.10.6 [CI SKIP] 2025-09-01 00:41:34 +00:00
TrainDoctor
779b1ae0de fix(ci): update to resolve deprecation in sem. rel. due to octokit 2025-08-31 17:40:34 -07:00
semantic-release-bot
366357d9a3 chore(release): 4.10.5 [CI SKIP] 2025-08-20 19:36:27 +00:00
AAGaming
37dd88513e fix(DialogHeader): valve rewrote component on beta, update filter to search for both versions 2025-08-20 15:35:17 -04:00
semantic-release-bot
ed87f73602 chore(release): 4.10.4 [CI SKIP] 2025-07-13 06:23:11 +00:00
AAGaming
3d5de65077 fix(modals): fix ModalRoot on beta
Co-Authored-By: junkrunner <eben@sdk.co.nz>
2025-07-13 02:22:21 -04:00
semantic-release-bot
33d224bc3f chore(release): 4.10.3 [CI SKIP] 2025-07-09 03:21:17 +00:00
shadow
aa0678c857 fix(modals): fix <ConfirmModal /> for new Steam beta (#123) 2025-07-08 23:20:45 -04:00
semantic-release-bot
39fdde9481 chore(release): 4.10.2 [CI SKIP] 2025-06-28 18:03:43 +00:00
AAGaming
a4627a70c5 fix(SteamClient): export shared types for enums 2025-06-28 14:03:04 -04:00
semantic-release-bot
1f6611a10f chore(release): 4.10.1 [CI SKIP] 2025-06-15 19:51:28 +00:00
TrainDoctor
c149711265 Merge pull request #119 from shdwmtr/main
fix: fix <Menu /> for new Steam beta
2025-06-15 12:50:57 -07:00
shadow
7a4c2af6ec chore: improve optional chaining && nullish coalescing 2025-06-15 16:31:26 -03:00
shadow
fc5cde95d6 fix: fix <Menu /> for new Steam Client Beta 2025-06-15 16:20:28 -03:00
semantic-release-bot
c4579c8307 chore(release): 4.10.0 [CI SKIP] 2025-04-23 15:21:21 +00:00
Yao Chung Hu
131ab4cc11 feat(globals): Steam client reference (#92)
* change: Add SteamClient types

Steam Version:  1690583737
Steam Client Build Date:  Sat, Jul 29 02:44 UTC -08:00

* new: Document some SteamClient interfaces

Using the SDH discord server, GitHub and some personal findings, I tried to document as much SteamClient functions as I can.

* new: Document some SteamClient interfaces - part 2

* new: Document some SteamClient interfaces - part 3

* new: Document some SteamClient interfaces - part 4

* new: Document some SteamClient interfaces - part 5

* new: Document some SteamClient interfaces - part 6

* new: Document some SteamClient interfaces - part 7

* change: Update interfaces to Steam Version 1691097434

Steam Version:  1691097434
Steam Client Build Date:  Fri, Aug 4 04:54 UTC -08:00

* new: Document some SteamClient interfaces - part 8

* new: Document some SteamClient interfaces - part 9

* change: Update interfaces to Steam Version 1694466342

Steam Version:  1694466342
Steam Client Build Date:  Tue, Sep 12 04:59 UTC +08:00

* new: Document some SteamClient interfaces - part 10

* new: Document some SteamClient interfaces - part 11

* new: Document some SteamClient interfaces - part 12

* new: Document some SteamClient interfaces - part 13

* new: Document some SteamClient interfaces - part 14

* change: Update interfaces to Steam Version 1698260427

Steam Version:  1698260427
Steam Client Build Date:  Thu, Oct 26 2:40 AM UTC -08:00

* new: Document some SteamClient interfaces - part 10

* change: Update interfaces to Steam Version 1700160213

Steam Version:  1700160213
Steam Client Build Date:  Fri, Nov 17 2:23 AM UTC +08:00

* docs(SteamClient): document some interfaces

* change: Fix formatting style

* docs(SteamClient): document protobufs & System.Report

* docs(SteamClient): document CMsgMonitorInfo

* docs(SteamClient): document remaining messages

* docs(SteamClient): update interfaces to Steam Version 1702515219
Steam Version:  1702515219
Steam Client Build Date:  Thu, Dec 14 00:36 UTC -08:00

* docs(SteamClient): update interfaces to Steam Version 1704329464
Steam Version:  1704329464
Steam Client Build Date:  Thu, Jan 4 12:23 AM UTC -08:00

* docs(SteamClient): update interfaces to Steam Version 1705630720
Steam Version:  1705630720
Steam Client Build Date:  Fri, Jan 19 1:58 AM UTC -08:00

* docs(SteamClient): update interfaces to Steam Version 1706914901
Steam Version:  1706914901
Steam Client Build Date:  Fri, Feb 2 8:46 PM UTC -08:00

* docs(SteamClient): update interfaces to Steam Version 1709920887
Steam Version:  1709920887
Steam Client Build Date:  Thu, Mar 7 5:29 PM UTC -08:00

* docs(SteamClient): update interfaces to Steam Version 1710786209
Steam Version:  1710786209
Steam Client Build Date:  Mon, Mar 18 7:07 PM UTC -08:00

* docs(SteamClient): document even more interfaces

* new: Document some SteamClient interfaces - part 15

* Fix QAM button bit number

* new: Document some SteamClient interfaces - part 16

* change: Update interfaces to Steam Version 1714854927

Steam Version: 1714854927
Steam Client Build Date:  Sat, May 4 7:11 AM UTC -08:00

* change: Refactor and reorganize SteamClient

* docs(SteamClient): document more notification interfaces

* docs(SteamClient): document more interfaces

* docs(SteamClient): add some descriptions

* docs(SteamClient): remove duplicate types

* change: Fix rebase errors

* Updated ControllerStateChange

* Fix typo

* docs(SteamClient): update interfaces to Steam Version 1723601482

Steam Version:  1723601482
Steam Client Build Date:  Wed, Aug 14 01:51 UTC -08:00

* docs(SteamClient): update interfaces to 1725654351

* docs(SteamClient): update interfaces to 1730416762

* docs(SteamClient): forgot some stuff

* docs(SteamClient): add missed interfaces

* docs(SteamClient): update enums

* docs(SteamClient): add some things and move stuff around

* docs(SteamClient): move shared enums/interfaces to a separate file

* docs(SteamClient): add more stuff

* docs(SteamClient): reword some things

* docs(SteamClient): new BrowserView things

* docs(SteamClient): review fixes

* move to src/globals

---------

Co-authored-by: ricewind012 <58827198+ricewind012@users.noreply.github.com>
Co-authored-by: Party Wumpus <48649272+PartyWumpus@users.noreply.github.com>
Co-authored-by: Xander <xander@isxander.dev>
2025-04-23 11:20:48 -04:00
semantic-release-bot
7e7a325cd9 chore(release): 4.9.2 [CI SKIP] 2025-03-29 13:02:17 +00:00
Lukas Senionis
41d0555d31 fix(SidebarNavigation): change title to ReactNode type (#117) 2025-03-29 09:01:51 -04:00
semantic-release-bot
716f947710 chore(release): 4.9.1 [CI SKIP] 2025-01-29 04:57:54 +00:00
TrainDoctor
ed7ba9bbb6 Merge pull request #116 from ricewind012/patch-1
fix(Menu): add missing arg to showContextMenu and document it
2025-01-28 20:57:28 -08:00
ricewind012
c03db3e0d5 fix(Menu): add missing arg to showContextMenu and document it 2025-01-25 12:23:52 +00:00
TrainDoctor
aef063d2e2 Merge pull request #112 from SteamDeckHomebrew/dependabot/npm_and_yarn/micromatch-4.0.8
chore(deps): bump micromatch from 4.0.7 to 4.0.8
2025-01-14 12:40:51 -08:00
dependabot[bot]
33e5d8541d chore(deps): bump cross-spawn from 7.0.3 to 7.0.6 (#114)
Bumps [cross-spawn](https://github.com/moxystudio/node-cross-spawn) from 7.0.3 to 7.0.6.
- [Changelog](https://github.com/moxystudio/node-cross-spawn/blob/master/CHANGELOG.md)
- [Commits](https://github.com/moxystudio/node-cross-spawn/compare/v7.0.3...v7.0.6)

---
updated-dependencies:
- dependency-name: cross-spawn
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-10 07:43:59 -05:00
semantic-release-bot
9c34100215 chore(release): 4.9.0 [CI SKIP] 2025-01-02 20:11:27 +00:00
Party Wumpus
a1507ac55b Merge pull request #115 from alvaro-cuesta/feat/menu-separator
feat: add `MenuSeparator` component
2025-01-02 20:11:00 +00:00
Álvaro Cuesta
484a882015 feat: add MenuSeparator component 2024-12-27 14:30:35 +01:00
semantic-release-bot
200125cb98 chore(release): 4.8.3 [CI SKIP] 2024-12-14 03:28:48 +00:00
AAGaming
5ab8472ffe Merge branch 'main' of github.com:steamdeckhomebrew/decky-frontend-lib 2024-12-13 22:28:22 -05:00
AAGaming
a43cd7f3c5 fix(utils): add new nav root name 2024-12-13 22:27:26 -05:00
semantic-release-bot
41401b1d01 chore(release): 4.8.2 [CI SKIP] 2024-11-20 19:50:37 +00:00
Lukas Senionis
8117693427 fix(Footer): add missing focus/nav properties (#113) 2024-11-20 14:50:10 -05:00
dependabot[bot]
d80c047580 chore(deps): bump micromatch from 4.0.7 to 4.0.8
Bumps [micromatch](https://github.com/micromatch/micromatch) from 4.0.7 to 4.0.8.
- [Release notes](https://github.com/micromatch/micromatch/releases)
- [Changelog](https://github.com/micromatch/micromatch/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/micromatch/compare/4.0.7...4.0.8)

---
updated-dependencies:
- dependency-name: micromatch
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-15 17:46:54 +00:00
semantic-release-bot
d6ecba605d chore(release): 4.8.1 [CI SKIP] 2024-10-11 19:00:25 +00:00
AAGaming
bf58e08e4b fix(DialogCheckbox): prevent some potential issues in dialogcheckbox 2024-10-11 14:59:42 -04:00
AAGaming
e965816144 fix(*): prevent issues where toString may not be a function (what) 2024-10-11 14:47:27 -04:00
semantic-release-bot
59ce9e450c chore(release): 4.8.0 [CI SKIP] 2024-10-05 03:52:37 +00:00
AAGaming
980df687e7 fix(components/Router): dont break in desktop ui 2024-10-04 23:52:04 -04:00
AAGaming
bea9ac7774 fix(components/Modal): dont break in desktop ui 2024-10-04 23:51:49 -04:00
AAGaming
063dedbbc1 feat(utils/react): add some window-related utils 2024-10-04 23:51:36 -04:00
semantic-release-bot
723e2ff76e chore(release): 4.7.4 [CI SKIP] 2024-10-04 17:16:01 +00:00
AAGaming
241b22cad7 fix(DialogCheckbox): don't access getters to prevent their side effects from breaking the component 2024-10-04 13:15:29 -04:00
semantic-release-bot
93c59d8026 chore(release): 4.7.3 [CI SKIP] 2024-10-03 20:50:33 +00:00
AAGaming
0f9fb5a3b8 fix(components): fix missing components on oct 2 2024 beta 2024-10-03 16:46:50 -04:00
semantic-release-bot
8c6043b5a7 chore(release): 4.7.2 [CI SKIP] 2024-09-16 23:30:08 +00:00
AAGaming
3aa07dc9ce fix(utils): fix potential race condition in findSP 2024-09-16 19:29:38 -04:00
semantic-release-bot
3f335b3583 chore(release): 4.7.1 [CI SKIP] 2024-08-08 18:18:52 +00:00
AAGaming
4c97097757 fix(utils/react): fix potential race condition in injectFCTrampoline 2024-08-08 14:18:19 -04:00
AAGaming
1ee43e86d6 chore(utils/react): get rid of ts-expect-error 2024-08-03 13:50:33 -04:00
AAGaming
db6ab9c448 chore(docs): add note to CloseSideMenus 2024-07-28 18:51:08 -04:00
semantic-release-bot
4646f22b0c chore(release): 4.7.0 [CI SKIP] 2024-07-28 22:18:51 +00:00
AAGaming
7eb484d55c feat(router): support desktop bpm overlay 2024-07-28 18:17:52 -04:00
AAGaming
5164f980b3 chore(stores): add SteamUIStore, securitystore 2024-07-28 18:15:51 -04:00
AAGaming
0457feec95 chore(tabs): port back to normal find 2024-07-28 18:15:34 -04:00
semantic-release-bot
73b8d52c7f chore(release): 4.6.0 [CI SKIP] 2024-07-26 18:33:10 +00:00
AAGaming
2b8d2ae4db feat(classMapper): add findClassByName back 2024-07-26 14:32:34 -04:00
AAGaming
48de8928e4 chore(deprecation): deprecate useQuickAccessVisible as it has been moved to @decky/api 2024-07-26 14:07:58 -04:00
semantic-release-bot
d60715b755 chore(release): 4.5.0 [CI SKIP] 2024-07-24 04:59:59 +00:00
AAGaming
a370c1f7d3 feat(classMapper): add classModuleMap, make findClass require ID 2024-07-24 00:59:28 -04:00
AAGaming
d83bada4af feat(webpack): refactor to prepare for classMapper changes 2024-07-24 00:59:04 -04:00
semantic-release-bot
1f7178f175 chore(release): 4.4.0 [CI SKIP] 2024-07-18 04:55:06 +00:00
AAGaming
44fdf9ed3b feat(utils/react): add injectFCTrampoline 2024-07-18 00:54:29 -04:00
semantic-release-bot
1b5fd78bc2 chore(release): 4.3.1 [CI SKIP] 2024-07-17 23:30:02 +00:00
AAGaming
d64c42ac31 fix(Menu/MenuGroup): rewrite filter to work on beta and prevent future errors 2024-07-17 19:29:25 -04:00
semantic-release-bot
0ec60edf4d chore(release): 4.3.0 [CI SKIP] 2024-07-09 06:38:22 +00:00
AAGaming
2c3a9f81de feat(utils): react tree patching api 2024-07-09 02:37:46 -04:00
semantic-release-bot
00079a71cc chore(release): 4.2.2 [CI SKIP] 2024-07-04 05:18:57 +00:00
AAGaming
c04f024b34 fix(SteamSpinner): add background option 2024-07-04 01:18:27 -04:00
AAGaming
5dae77b003 chore(ci): update actions to remove deprecation warnings 2024-06-29 17:53:00 -04:00
semantic-release-bot
6b8c28e69c chore(release): 4.2.1 [CI SKIP] 2024-06-27 04:17:41 +00:00
AAGaming
3ef9648355 fix(errorboundary): work around broken react types
for the billionth time
2024-06-27 00:16:14 -04:00
semantic-release-bot
0da85355c2 chore(release): 4.2.0 [CI SKIP] 2024-06-27 04:07:06 +00:00
AAGaming
dcdbb2d6c7 feat(components): add ErrorBoundary 2024-06-27 00:06:32 -04:00
semantic-release-bot
58e3d35e1e chore(release): 4.1.1 [CI SKIP] 2024-06-27 03:19:49 +00:00
Ava Johnson
bd1dc85b92 fix(ReorderableList): avoid mutating props (#109)
Co-authored-by: AAGaming <aagaming@riseup.net>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-26 23:19:18 -04:00
semantic-release-bot
aa78f4c0a2 chore(release): 4.1.0 [CI SKIP] 2024-06-27 03:11:23 +00:00
AAGaming
b04044451a feat(release): release v4.1.0 2024-06-26 23:10:47 -04:00
semantic-release-bot
5d8f141114 chore(release): 4.0.1 [CI SKIP] 2024-06-27 03:10:04 +00:00
AAGaming
2bfe62409f fix(release): empty commit to bump to v4.0.1 2024-06-26 23:09:36 -04:00
semantic-release-bot
f5cefa9eea chore(release): 4.0.0 [CI SKIP] 2024-06-27 03:08:08 +00:00
AAGaming
abfd2c0105 fix(ci): temp remove npm publisher
BREAKING CHANGE: v4 release
2024-06-26 23:07:29 -04:00
AAGaming
8cb7c273eb fix(ci): empty commit so semantic-release shuts up
BREAKING CHANGE: v4 release
2024-06-26 23:03:39 -04:00
AAGaming
c440fa76b1 chore(ci): empty commit so semantic-release shuts up 2024-06-26 23:00:30 -04:00
AAGaming
4e4859deb6 chore(ci): empty commit so semantic-release shuts up 2024-06-26 22:56:50 -04:00
AAGaming
9579c41c6d chore(ci): empty commit so semantic-release shuts up 2024-06-26 22:53:34 -04:00
AAGaming
4dd6afd91b chore(ci): add v4-dev to release branches 2024-06-26 22:41:52 -04:00
AAGaming
b092d49bf8 chore(ci): return of the CI 2024-06-26 22:39:30 -04:00
AAGaming
7e0cb153b1 fix(*): fixes for jun 26 beta 2024-06-26 22:28:58 -04:00
AAGaming
b1e503853f fix(package): unbreak react 2024-06-13 18:26:38 -04:00
AAGaming
832ad697c7 chore(package): v4.0.3 2024-06-13 18:23:06 -04:00
AAGaming
62b454f712 fix(package): pin react properly 2024-06-13 18:22:18 -04:00
AAGaming
ea19d62dab fix(package): fix ts memes 2024-06-13 17:00:37 -04:00
AAGaming
9f262097b9 fix(Menu): work around ConfigContext error 2024-06-13 15:53:06 -04:00
AAGaming
e6e8c91ec7 fix(components): forgot one 2024-05-27 13:20:03 -04:00
AAGaming
688c7471cd fix(components): fix missing children prop
also remove unnessecary use of tsx
2024-05-27 13:19:11 -04:00
AAGaming
6c1b12b95f chore(ci): disable on v4 for now 2024-05-27 13:15:49 -04:00
semantic-release-bot
0dfb2cedb6 chore(release): 3.26.1 [CI SKIP] 2024-05-25 23:15:00 +00:00
AAGaming
2a78cc0116 fix(Field): add children prop 2024-05-25 19:14:30 -04:00
AAGaming
1716782183 chore(package): ver bump [skip ci] 2024-05-24 18:04:31 -04:00
AAGaming
0cd498beac fix(plugin): shim definePlugin for now [ci skip] 2024-05-24 18:04:05 -04:00
AAGaming
b728d90263 chore(plugin): delete 2024-05-24 17:10:51 -04:00
AAGaming
3384d2910d chore(*): fix version lol 2024-05-24 16:35:31 -04:00
semantic-release-bot
78c4cdb9e9 chore(release): 3.26.0 [CI SKIP] 2024-05-24 20:24:30 +00:00
AAGaming
d20659e073 chore(releaserc): add v4-dev branch 2024-05-24 16:23:59 -04:00
AAGaming
d91f49e728 fix(utils/react): shut it ts 2024-05-24 16:22:51 -04:00
AAGaming
735080fee3 chore(*): init v4 package.json and release.yaml 2024-05-24 16:21:58 -04:00
jbofill
86e6e4c0f5 chore(static-classes): add/fix/update css classes (#102) 2024-05-24 16:17:48 -04:00
shadow
46b1d6e8ea Dropdown contextMenuPositionOptions prop definition (#104) 2024-05-24 16:17:12 -04:00
AAGaming
a7635b6305 chore(*): run prettier 2024-05-12 15:48:13 -04:00
AAGaming
bffd530bda feat(*): add v4 webpack api and port everything to it
also restructures a bunch
2024-05-12 15:45:26 -04:00
AAGaming
9c79187d37 chore(package): init v4 2024-05-12 12:43:26 -04:00
semantic-release-bot
29e6439115 chore(release): 3.25.0 [CI SKIP] 2024-03-09 22:17:49 +00:00
AAGaming
a8eeb917e2 feat(classMapper): add class mapper
thanks valve
2024-03-09 17:13:39 -05:00
AAGaming
17b99dfed8 fix(staticclasses): unbreak on latest beta
unsure if this works for all of them but it works Enough
2024-03-09 17:13:05 -05:00
AAGaming
9ef419cee1 chore(modal): remove useless comments 2024-02-03 00:49:11 -05:00
semantic-release-bot
5aad952936 chore(release): 3.24.5 [CI SKIP] 2024-02-03 05:32:01 +00:00
AAGaming
c2b0fad298 fix(finds): make modal and scroll components work on latest betaa
THEY INCREASED THE MINIFIER PRESET AAAAAAAAAAAAAAAAAAAAAAAAAA
2024-02-03 00:31:29 -05:00
semantic-release-bot
4d4cfedfe0 chore(release): 3.24.4 [CI SKIP] 2024-01-22 19:56:22 +00:00
AAGaming
bb12921863 fix(types): fix incorrect as on many components leading to any types 2024-01-22 14:55:52 -05:00
semantic-release-bot
223739af25 chore(release): 3.24.3 [CI SKIP] 2024-01-20 03:44:07 +00:00
AAGaming
95d977df45 fix(router): wait 2s if internal navigators init fails 2024-01-19 22:43:38 -05:00
semantic-release-bot
0f2692a3f2 chore(release): 3.24.2 [CI SKIP] 2024-01-20 01:40:41 +00:00
Beebles
ebf496bf61 fix(navigation): Fix on chromium 109 (#100) 2024-01-19 18:40:11 -07:00
semantic-release-bot
0a5170e412 chore(release): 3.24.1 [CI SKIP] 2023-12-13 03:08:36 +00:00
AAGaming
11dd82bbb1 fix(utils/react): support react 18, add getReactRoot 2023-12-12 22:07:59 -05:00
semantic-release-bot
153bb209d1 chore(release): 3.24.0 [CI SKIP] 2023-12-03 22:29:55 +00:00
Lukas Senionis
e27b638d26 feat(static-classes): add BasicAppDetailsSectionStylerClasses (#99) 2023-12-03 17:29:30 -05:00
AAGaming
7d287f10d6 chore(releaserc): fix typo 2023-11-26 15:01:54 -05:00
AAGaming
9925bc8cfb chore(releaserc): add docs(steamclient) release patch target 2023-11-26 15:01:33 -05:00
semantic-release-bot
179a93d5e1 chore(release): 3.23.1 [CI SKIP] 2023-11-09 19:45:32 +00:00
AAGaming
5203ce348a fix(webpack): don't break if a module fails to load
doesn't matter now but could in the future so might as well fix it now
2023-11-09 14:44:41 -05:00
dependabot[bot]
3f47b5ccce chore(deps-dev): bump @babel/traverse from 7.21.4 to 7.23.2 (#98) 2023-10-20 13:58:41 +00:00
semantic-release-bot
d22d32677b chore(release): 3.23.0 [CI SKIP] 2023-10-11 16:06:34 +00:00
jbofill
30e319425b feat(static-classes): add more css classes (#94) 2023-10-11 12:06:01 -04:00
semantic-release-bot
aebdfaa089 chore(release): 3.22.1 [CI SKIP] 2023-10-10 13:38:32 +00:00
TrainDoctor
de914b1a35 Merge pull request #97 from FrogTheFrog/patch-29
fix(useQuickAccessVisible): use the "Page Visibility API" instead of focus/blur
2023-10-10 06:37:52 -07:00
Lukas Senionis
4c4fda47e3 fix(useQuickAccessVisible): use the "Page Visibility API" instead of focus/blur 2023-10-09 21:38:34 +03:00
141 changed files with 18078 additions and 6465 deletions

View File

@@ -1,9 +1,10 @@
name: Generate docs
on:
push:
branches:
- main
workflow_dispatch:
# push:
# branches:
# - main
jobs:
release:
@@ -11,19 +12,19 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Setup | Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 0
path: lib
- name: Setup | Checkout wiki
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
repository: SteamDeckHomebrew/wiki
path: wiki
ssh-key: ${{ secrets.SSH_DEPLOY_KEY }}
persist-credentials: true
- name: Setup | Node.js
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: 18
- name: Setup | Dependencies
@@ -51,4 +52,4 @@ jobs:
ssh: true
directory: ./wiki
repository: SteamDeckHomebrew/wiki
branch: main
branch: main

View File

@@ -6,19 +6,23 @@ on:
branches:
- main
permissions:
id-token: write
contents: write
jobs:
release:
name: Release
runs-on: ubuntu-22.04
steps:
- name: Setup | Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup | Node.js
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: 16
node-version: 24
- name: Setup | Dependencies
run: npm i -g pnpm && pnpm i --frozen-lockfile
- name: Build
@@ -29,5 +33,4 @@ jobs:
if: github.event_name != 'pull_request'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: pnpm exec semantic-release

View File

@@ -1,4 +1 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx --no -- commitlint --edit "${1}"

View File

@@ -1,13 +1,14 @@
{
"branches": ["main", "dev"],
"branches": ["main", "v4-dev"],
"plugins": [
[
"@semantic-release/commit-analyzer",
{
"preset": "angular",
"releaseRules": [
{"type": "chore", "scope": "classes", "release": "patch"},
{"type": "*", "scope": "docs", "release": false}
{ "type": "chore", "scope": "classes", "release": "patch" },
{ "type": "docs", "scope": "steamclient", "release": "patch" },
{ "type": "*", "scope": "docs", "release": false }
]
}
],

26
.vscode/tasks.json vendored
View File

@@ -1,15 +1,13 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "dev",
"problemMatcher": [
"$tsc-watch"
],
"label": "npm: dev",
"detail": "tsc -b -w",
"isBackground": true
}
]
}
"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,385 @@
## [4.11.4](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v4.11.3...v4.11.4) (2026-05-15)
### Bug Fixes
* **dropdown:** hack to fix styling in dropdownitem ([e44664c](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/e44664c9704b3b284284619bc26cf6a910890136))
* **Field:** fix filter for field on beta ([ac8da8e](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/ac8da8e9b650cfcabcd3e5752e1475e91e7edf7a))
## [4.11.3](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v4.11.2...v4.11.3) (2026-03-22)
### Bug Fixes
* **Tabs:** update for latest beta ([#129](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/129)) ([3126dd3](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/3126dd3e040eaef00eb0362b69efb143d7e01030))
## [4.11.2](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v4.11.1...v4.11.2) (2026-03-21)
### Bug Fixes
* **ci:** microsoft ([aabc522](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/aabc5227400f1660ddcb7917fa8f305ad2fce3d7))
* **ci:** microsoft stinks ([66e0afc](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/66e0afccae6803ee57d817621340d09a12ba75d8))
* **ci:** update semantic release ([772a855](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/772a85523b8c4bbf80ccd4827a89801e3a51cfd6))
* **ci:** use new npm publishing auth ([77e6acd](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/77e6acd828b1d0f0978356292b1c46bfc3b8e422))
* **components:** update for latest beta ([261162c](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/261162c8bceecc50afad07d57a78b85b98936fe1))
## [4.11.1](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v4.11.0...v4.11.1) (2025-11-27)
### Bug Fixes
* **webpack:** ignore window module (lol), ignore filter errors ([7242c69](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/7242c697580f3a6a6d373eaacc4fb83ccff9bd2a))
# [4.11.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v4.10.6...v4.11.0) (2025-10-15)
### Features
* react 19 support update ([6e443c0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/6e443c06d3b7f2790da8b44a5c907517fcf12152))
## [4.10.6](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v4.10.5...v4.10.6) (2025-09-01)
### Bug Fixes
* **ci:** update to resolve deprecation in sem. rel. due to octokit ([779b1ae](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/779b1ae0defac84dd0d93517858d5aa5e51a2328))
## [4.10.5](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v4.10.4...v4.10.5) (2025-08-20)
### Bug Fixes
* **DialogHeader:** valve rewrote component on beta, update filter to search for both versions ([37dd885](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/37dd88513ed7360d3bcdc99630d6cb0c8d31db3e))
## [4.10.4](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v4.10.3...v4.10.4) (2025-07-13)
### Bug Fixes
* **modals:** fix ModalRoot on beta ([3d5de65](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/3d5de65077098ff3fb5192cb55fb1534336411f5))
## [4.10.3](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v4.10.2...v4.10.3) (2025-07-09)
### Bug Fixes
* **modals:** fix `<ConfirmModal />` for new Steam beta ([#123](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/123)) ([aa0678c](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/aa0678c857e07f58de58e5d20565bf2718fff6dc))
## [4.10.2](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v4.10.1...v4.10.2) (2025-06-28)
### Bug Fixes
* **SteamClient:** export shared types for enums ([a4627a7](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/a4627a70c5a60a42cd39690b3b03f2c1d79ee3b4))
## [4.10.1](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v4.10.0...v4.10.1) (2025-06-15)
### Bug Fixes
* fix <Menu /> for new Steam Client Beta ([fc5cde9](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/fc5cde95d6475c82856769c86ab648dfb22a4b10))
# [4.10.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v4.9.2...v4.10.0) (2025-04-23)
### Features
* **globals:** Steam client reference ([#92](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/92)) ([131ab4c](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/131ab4cc1190e2bd0cf561fe6dd70049a7921fc5))
## [4.9.2](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v4.9.1...v4.9.2) (2025-03-29)
### Bug Fixes
* **SidebarNavigation:** change title to ReactNode type ([#117](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/117)) ([41d0555](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/41d0555d314f9f68d3cdcc45f454f13b952b4581))
## [4.9.1](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v4.9.0...v4.9.1) (2025-01-29)
### Bug Fixes
* **Menu:** add missing arg to showContextMenu and document it ([c03db3e](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/c03db3e0d55f2ba14a8d82ba1380e9838769efe1))
# [4.9.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v4.8.3...v4.9.0) (2025-01-02)
### Features
* add MenuSeparator component ([484a882](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/484a882015ceb51c2c80e08151e79056e751ac15))
## [4.8.3](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v4.8.2...v4.8.3) (2024-12-14)
### Bug Fixes
* **utils:** add new nav root name ([a43cd7f](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/a43cd7f3c56a2e30e332417e5b4abde8f4492be1))
## [4.8.2](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v4.8.1...v4.8.2) (2024-11-20)
### Bug Fixes
* **Footer:** add missing focus/nav properties ([#113](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/113)) ([8117693](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/8117693427e4dba2f3b5bd24f36704d8d5e65ae2))
## [4.8.1](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v4.8.0...v4.8.1) (2024-10-11)
### Bug Fixes
* **DialogCheckbox:** prevent some potential issues in dialogcheckbox ([bf58e08](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/bf58e08e4b5b183737c9ad43a858b642ce593f93))
* prevent issues where toString may not be a function (what) ([e965816](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/e96581614471524cbf91bf962742df867ba3c3e9))
# [4.8.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v4.7.4...v4.8.0) (2024-10-05)
### Bug Fixes
* **components/Modal:** dont break in desktop ui ([bea9ac7](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/bea9ac777452b883c2f3f3ebf8510c22c4577cd0))
* **components/Router:** dont break in desktop ui ([980df68](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/980df687e7b7bf389f478e831f992fb9475eeb15))
### Features
* **utils/react:** add some window-related utils ([063dedb](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/063dedbbc1573a825571c675bf6bfa8a1bc1c6cd))
## [4.7.4](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v4.7.3...v4.7.4) (2024-10-04)
### Bug Fixes
* **DialogCheckbox:** don't access getters to prevent their side effects from breaking the component ([241b22c](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/241b22cad711621a1695dfd11da857f13c3fffdf))
## [4.7.3](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v4.7.2...v4.7.3) (2024-10-03)
### Bug Fixes
* **components:** fix missing components on oct 2 2024 beta ([0f9fb5a](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/0f9fb5a3b8ef4f9978025a28323ab080fb0e7a4c))
## [4.7.2](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v4.7.1...v4.7.2) (2024-09-16)
### Bug Fixes
* **utils:** fix potential race condition in findSP ([3aa07dc](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/3aa07dc9ce798ff8d1148424ee9e8a8bf2ba78c6))
## [4.7.1](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v4.7.0...v4.7.1) (2024-08-08)
### Bug Fixes
* **utils/react:** fix potential race condition in injectFCTrampoline ([4c97097](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/4c97097757919580a380b70785e6c161de6b03cc))
# [4.7.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v4.6.0...v4.7.0) (2024-07-28)
### Features
* **router:** support desktop bpm overlay ([7eb484d](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/7eb484d55c6be6e7844878eb47eda55591a6cf51))
# [4.6.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v4.5.0...v4.6.0) (2024-07-26)
### Features
* **classMapper:** add findClassByName back ([2b8d2ae](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/2b8d2ae4dbd9a0c4a59a43be0101a0a8fe1c518f))
# [4.5.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v4.4.0...v4.5.0) (2024-07-24)
### Features
* **classMapper:** add classModuleMap, make findClass require ID ([a370c1f](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/a370c1f7d3dca0db56a346c98c28ed9681a415e0))
* **webpack:** refactor to prepare for classMapper changes ([d83bada](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/d83bada4af2d16c750955de9a52f94a0080a2c14))
# [4.4.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v4.3.1...v4.4.0) (2024-07-18)
### Features
* **utils/react:** add injectFCTrampoline ([44fdf9e](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/44fdf9ed3b9a676a88b0ddc6a1c2c89d46ff7651))
## [4.3.1](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v4.3.0...v4.3.1) (2024-07-17)
### Bug Fixes
* **Menu/MenuGroup:** rewrite filter to work on beta and prevent future errors ([d64c42a](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/d64c42ac310d3c3266c4ff610d9ec5ab6c7707b6))
# [4.3.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v4.2.2...v4.3.0) (2024-07-09)
### Features
* **utils:** react tree patching api ([2c3a9f8](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/2c3a9f81de0b63364bb31f4a4dd8e559784ece16))
## [4.2.2](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v4.2.1...v4.2.2) (2024-07-04)
### Bug Fixes
* **SteamSpinner:** add background option ([c04f024](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/c04f024b34b1148c965850965127f9fd44204157))
## [4.2.1](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v4.2.0...v4.2.1) (2024-06-27)
### Bug Fixes
* **errorboundary:** work around broken react types ([3ef9648](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/3ef96483550020cecf656b94a73d2bb9313bda07))
# [4.2.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v4.1.1...v4.2.0) (2024-06-27)
### Features
* **components:** add ErrorBoundary ([dcdbb2d](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/dcdbb2d6c7c0b72197f04153d7c3e73e9e71ea5c))
## [4.1.1](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v4.1.0...v4.1.1) (2024-06-27)
### Bug Fixes
* **ReorderableList:** avoid mutating props ([#109](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/109)) ([bd1dc85](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/bd1dc85b9202c8ec6ca994177417574fdd71cbd7))
# [4.1.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v4.0.1...v4.1.0) (2024-06-27)
### Features
* **release:** release v4.1.0 ([b040444](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/b04044451a9dc3633fe624e47dd58c7ea206d0a3))
## [4.0.1](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v4.0.0...v4.0.1) (2024-06-27)
### Bug Fixes
* **release:** empty commit to bump to v4.0.1 ([2bfe624](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/2bfe62409f775a69124e0f2e853ae0b1668a9c36))
# [4.0.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.25.0...v4.0.0) (2024-06-27)
### Bug Fixes
* **ci:** empty commit so semantic-release shuts up ([8cb7c27](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/8cb7c273eb61c1f949844291b2864c11bf746058))
* **ci:** temp remove npm publisher ([abfd2c0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/abfd2c010508ddf6e18149374dc52b50402ffb4c))
* **components:** fix missing children prop ([688c747](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/688c7471cde96bf9e9a71c47d19bd63cff7a66b3))
* **components:** forgot one ([e6e8c91](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/e6e8c91ec7ea711f6c147f28e300f745dcbd24f4))
* **Field:** add children prop ([2a78cc0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/2a78cc011671ab123fb6356f54102e531a7953a8))
* fixes for jun 26 beta ([7e0cb15](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/7e0cb153b197267f49b5e1f513b54a880bf3710f))
* **Menu:** work around ConfigContext error ([9f26209](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/9f262097b9808a0366bb52cd24844371bbe65316))
* **package:** fix ts memes ([ea19d62](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/ea19d62dabfbecad97c1ab9384c676f8a018f96b))
* **package:** pin react properly ([62b454f](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/62b454f71255d5e1898677909a63612615e09083))
* **package:** unbreak react ([b1e5038](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/b1e503853ff6d39f1e86a6180f355e73b9fd6925))
* **plugin:** shim definePlugin for now [ci skip] ([0cd498b](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/0cd498beacbd23efa3b771880ff0a4df3f636836))
* **utils/react:** shut it ts ([d91f49e](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/d91f49e728b613e24b40d7e81d47fd1f3bd92372))
### Features
* add v4 webpack api and port everything to it ([bffd530](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/bffd530bda9a49aae603c323c0a2b4328eee968d))
### BREAKING CHANGES
* **ci:** v4 release
* **ci:** v4 release
## [3.26.1](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.26.0...v3.26.1) (2024-05-25)
### Bug Fixes
* **Field:** add children prop ([2a78cc0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/2a78cc011671ab123fb6356f54102e531a7953a8))
* **plugin:** shim definePlugin for now [ci skip] ([0cd498b](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/0cd498beacbd23efa3b771880ff0a4df3f636836))
# [3.26.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.25.0...v3.26.0) (2024-05-24)
### Bug Fixes
* **utils/react:** shut it ts ([d91f49e](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/d91f49e728b613e24b40d7e81d47fd1f3bd92372))
### Features
* add v4 webpack api and port everything to it ([bffd530](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/bffd530bda9a49aae603c323c0a2b4328eee968d))
# [3.25.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.24.5...v3.25.0) (2024-03-09)
### Bug Fixes
* **staticclasses:** unbreak on latest beta ([17b99df](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/17b99dfed8e4e146d0f4f5e78a950db5b10ae2b4))
### Features
* **classMapper:** add class mapper ([a8eeb91](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/a8eeb917e22ef72905d448e159d70375ebf77ba6))
## [3.24.5](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.24.4...v3.24.5) (2024-02-03)
### Bug Fixes
* **finds:** make modal and scroll components work on latest betaa ([c2b0fad](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/c2b0fad298512aa8778c677275bd497bd8f7b00e))
## [3.24.4](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.24.3...v3.24.4) (2024-01-22)
### Bug Fixes
* **types:** fix incorrect `as` on many components leading to any types ([bb12921](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/bb129218634b77ddb1d73b0fe38a91898073707c))
## [3.24.3](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.24.2...v3.24.3) (2024-01-20)
### Bug Fixes
* **router:** wait 2s if internal navigators init fails ([95d977d](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/95d977df452d3b73b007c98854deab1842fa6fbf))
## [3.24.2](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.24.1...v3.24.2) (2024-01-20)
### Bug Fixes
* **navigation:** Fix on chromium 109 ([#100](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/100)) ([ebf496b](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/ebf496bf61cffa1a5205b4a094fd2279011bffa9))
## [3.24.1](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.24.0...v3.24.1) (2023-12-13)
### Bug Fixes
* **utils/react:** support react 18, add getReactRoot ([11dd82b](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/11dd82bbb1814ac4d2fa9d381372e325daba2558))
# [3.24.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.23.1...v3.24.0) (2023-12-03)
### Features
* **static-classes:** add BasicAppDetailsSectionStylerClasses ([#99](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/99)) ([e27b638](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/e27b638d26e41332b1554dbd55ca0c55a1821138))
## [3.23.1](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.23.0...v3.23.1) (2023-11-09)
### Bug Fixes
* **webpack:** don't break if a module fails to load ([5203ce3](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/5203ce348afd727da0c8c52f6d8f8a16712f88d2))
# [3.23.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.22.1...v3.23.0) (2023-10-11)
### Features
* **static-classes:** add more css classes ([#94](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/94)) ([30e3194](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/30e319425bd4b0ee481dd7bd3245dacd90806afb))
## [3.22.1](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.22.0...v3.22.1) (2023-10-10)
### Bug Fixes
* **useQuickAccessVisible:** use the "Page Visibility API" instead of focus/blur ([4c4fda4](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/4c4fda47e3d9fd936b493c5965634a0ff443014f))
# [3.22.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.21.8...v3.22.0) (2023-08-09)

View File

@@ -20,8 +20,11 @@ This library can also theoretically be used to extend existing UI elements of th
### Getting Started (Developers)
If you would like a feature added to decky-frontend-lib, please request it via a Github issue.
If you would like a feature added to decky-frontend-lib, please request it via a Github issue.
If you want to start making a plugin with decky-frontend-lib, please direct your attention to the [decky-plugin-template](https://github.com/SteamDeckHomebrew/decky-plugin-template) repository.
This library can be found on [npm](https://www.npmjs.com/package/decky-frontend-lib) and as such you can pull it without a local copy for your project as needed.
Tips for fixing failing module finds after Steam updates:
- `Object.entries(DFL)` can point out any undefined exports

View File

@@ -1,3 +1,3 @@
module.exports = {
presets: [['@babel/preset-env', { targets: { node: 'current' } }], '@babel/preset-typescript',],
presets: [['@babel/preset-env', { targets: { node: 'current' } }], '@babel/preset-typescript'],
};

10
global.d.ts vendored Normal file
View File

@@ -0,0 +1,10 @@
import type * as React from 'react';
import type * as ReactDOM from 'react-dom';
import type * as JSXRuntime from 'react/jsx-runtime';
declare global {
interface Window {
SP_REACT: typeof React;
SP_REACTDOM: typeof ReactDOM;
SP_JSX: typeof JSXRuntime;
}
}

5
globals.d.ts vendored
View File

@@ -1,5 +0,0 @@
declare global {
interface Window {
SP_REACT: typeof React;
}
}

View File

@@ -1,7 +1,7 @@
{
"name": "decky-frontend-lib",
"version": "3.22.0",
"description": "A library for building decky plugins",
"name": "@decky/ui",
"version": "4.11.4",
"description": "A library for interacting with the Steam frontend in Decky plugins and elsewhere.",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"module": "dist/index.js",
@@ -29,7 +29,7 @@
"steam",
"components"
],
"author": "Jonas Dellinger <jonas@dellinger.dev>",
"author": "SteamDeckHomebrew Team",
"license": "LGPL-2.1",
"bugs": {
"url": "https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues"
@@ -41,29 +41,30 @@
}
},
"devDependencies": {
"@commitlint/cli": "^17.0.2",
"@commitlint/config-conventional": "^17.0.2",
"@commitlint/cz-commitlint": "^17.0.0",
"@semantic-release/changelog": "^6.0.1",
"@commitlint/cli": "^19.8.1",
"@commitlint/config-conventional": "^19.8.1",
"@commitlint/cz-commitlint": "^19.8.1",
"@semantic-release/changelog": "^6.0.3",
"@semantic-release/git": "^10.0.1",
"@types/jest": "^27.4.1",
"@types/react": "16.14.0",
"@types/react-router": "5.1.18",
"commitizen": "^4.2.4",
"husky": "^8.0.1",
"@types/jest": "^29.5.14",
"@types/react": "19.1.1",
"@types/react-dom": "19.1.1",
"@types/react-router": "5.1.20",
"commitizen": "^4.3.1",
"husky": "^9.1.7",
"import-sort-style-module": "^6.0.0",
"jest": "^27.5.1",
"minimist": "^1.2.6",
"prettier": "^2.7.1",
"jest": "^29.7.0",
"minimist": "^1.2.8",
"prettier": "^3.6.2",
"prettier-plugin-import-sort": "^0.0.7",
"semantic-release": "^19.0.3",
"semantic-release": "^25.0.3",
"shx": "^0.3.4",
"ts-jest": "^27.1.4",
"typedoc": "^0.23.24",
"typedoc-plugin-mdn-links": "^2.0.0",
"typedoc-plugin-missing-exports": "^1.0.0",
"ts-jest": "^29.4.1",
"typedoc": "^0.25.13",
"typedoc-plugin-mdn-links": "^3.3.8",
"typedoc-plugin-missing-exports": "^2.3.0",
"typedoc-wikijs-theme": "^1.0.5",
"typescript": "^4.9.5"
"typescript": "^5.9.2"
},
"pnpm": {
"peerDependencyRules": {
@@ -71,6 +72,12 @@
"react",
"react-dom"
]
},
"updateConfig": {
"ignoreDependencies": [
"react",
"react-dom"
]
}
},
"importSort": {
@@ -78,5 +85,9 @@
"style": "module",
"parser": "typescript"
}
},
"publishConfig": {
"registry": "https://registry.npmjs.org/",
"tag": "latest"
}
}

9041
pnpm-lock.yaml generated Executable file → Normal file

File diff suppressed because it is too large Load Diff

40
src/class-mapper.ts Normal file
View File

@@ -0,0 +1,40 @@
import { Module, ModuleID, createModuleMapping } from './webpack';
export interface ClassModule {
[name: string]: string;
}
export const classModuleMap: Map<ModuleID, ClassModule> = createModuleMapping((m: Module) => {
if (typeof m == 'object' && !m.__esModule) {
const keys = Object.keys(m);
// special case some libraries
if (keys.length == 1 && m.version) return false;
// special case localization
if (keys.length > 1000 && m.AboutSettings) return false;
return keys.length > 0 && keys.every((k) => !Object.getOwnPropertyDescriptor(m, k)?.get && typeof m[k] == 'string');
}
return false;
});
export const classMap = [...classModuleMap.values()];
export function findClass(id: string, name: string): string | void {
return classModuleMap.get(id)?.[name];
}
export function findClassByName(name: string): string | void {
return classMap.find((m) => m[name])?.[name];
}
export function findClassModule(filter: (module: any) => boolean): ClassModule | void {
return classMap.find((m) => filter(m));
}
export function unminifyClass(minifiedClass: string): string | void {
for (let m of classModuleMap.values()) {
for (let className of Object.keys(m)) {
if (m[className] == minifiedClass) return className;
}
}
}

View File

@@ -0,0 +1,16 @@
import { FC } from 'react';
import { CommonUIModule } from '../webpack';
import { ItemProps } from './Item';
import { createPropListRegex } from '../utils';
export interface ButtonItemProps extends ItemProps {
onClick?(e: MouseEvent): void;
disabled?: boolean;
}
const buttonItemRegex = createPropListRegex(["highlightOnFocus", "childrenContainerWidth"], false);
export const ButtonItem = Object.values(CommonUIModule).find(
(mod: any) =>
(mod?.render?.toString && buttonItemRegex.test(mod.render.toString())) ||
mod?.render?.toString?.().includes('childrenContainerWidth:"min"'),
) as FC<ButtonItemProps>;

View File

@@ -1,6 +1,6 @@
import { HTMLAttributes, ReactNode, RefAttributes, VFC } from 'react';
import { HTMLAttributes, ReactNode, RefAttributes, FC } from 'react';
import { findModuleChild } from '../webpack';
import { Export, findModuleExport } from '../webpack';
export interface CarouselProps extends HTMLAttributes<HTMLDivElement> {
autoFocus?: boolean;
@@ -20,9 +20,6 @@ export interface CarouselProps extends HTMLAttributes<HTMLDivElement> {
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>>;
export const Carousel = findModuleExport((e: Export) => e.render?.toString().includes('setFocusedColumn:')) as FC<
CarouselProps & RefAttributes<HTMLDivElement>
>;

View File

@@ -0,0 +1,14 @@
import { FC, ReactNode } from 'react';
import { Export, findModuleExport } from '../webpack';
export interface ControlsListProps {
alignItems?: 'left' | 'right' | 'center';
spacing?: 'standard' | 'extra';
children?: ReactNode;
}
export const ControlsList: FC<ControlsListProps> = findModuleExport(
(e: Export) =>
e?.toString && e.toString().includes('().ControlsListChild') && e.toString().includes('().ControlsListOuterPanel'),
);

View File

@@ -1,17 +1,18 @@
import { CSSProperties, FC, RefAttributes } from 'react';
import { CSSProperties, FC, ReactNode, RefAttributes } from 'react';
import { CommonUIModule } from '../webpack';
import { CommonUIModule, Module } from '../webpack';
import { FooterLegendProps } from './FooterLegend';
export interface DialogCommonProps extends RefAttributes<HTMLDivElement> {
style?: CSSProperties;
className?: string;
children?: ReactNode;
}
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.
*/
@@ -19,25 +20,25 @@ export interface DialogButtonProps extends DialogCommonProps, FooterLegendProps
/**
* 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
* 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.
*
* 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;
@@ -51,17 +52,37 @@ export interface DialogButtonProps extends DialogCommonProps, FooterLegendProps
}
const CommonDialogDivs = Object.values(CommonUIModule).filter(
(m: any) => typeof m === 'object' && m?.render?.toString().includes('"div",Object.assign({},'),
(m: any) => typeof m === 'object' &&
// New
(
m?.render?.toString().includes('jsx)("div",{...') ||
m?.render?.toString().includes('jsx)("div",Object.assign({},')
) ||
// Old
(
m?.render?.toString().includes('createElement("div",{...') ||
m?.render?.toString().includes('createElement("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];
try {
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];
} catch (e) {
console.error("[DFL:Dialog]: failed to render common dialog component", e);
return [null, null];
}
}),
);
export const DialogHeader = MappedDialogDivs.get('DialogHeader') as FC<DialogCommonProps>;
// Old | New
export const DialogHeader = (MappedDialogDivs.get('DialogHeader') || Object.values(CommonUIModule).find((component: Module) => {
const str = component?.render?.toString?.();
return str?.includes("role:\"heading\"") && str.includes(")(\"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>;
@@ -71,14 +92,11 @@ export const DialogControlsSection = MappedDialogDivs.get('DialogControlsSection
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'),
(mod: any) => mod?.render?.toString?.()?.includes('"DialogButton","_DialogLayout","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'),
(mod: any) => mod?.render?.toString?.()?.includes('"DialogButton","_DialogLayout","Secondary"')
) as FC<DialogButtonProps>;
// This is the "main" button. The Primary can act as a submit button,

View File

@@ -0,0 +1,33 @@
import { FC, ReactNode } from 'react';
import { findModuleExport } 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;
}
// Do not access KeyDown, SetChecked, Toggle here as they are getters and accessing them outside of a render breaks them globally
export const DialogCheckbox = findModuleExport(e =>
e?.prototype &&
typeof e?.prototype == "object" &&
"GetPanelElementProps" in e?.prototype &&
"SetChecked" in e?.prototype &&
"Toggle" in e?.prototype &&
// beta || stable as of oct 2 2024
(e?.prototype?.render?.toString?.().includes('="DialogCheckbox"') || (
e.contextType &&
e.prototype?.render?.toString?.().includes('fallback:')
))
) as FC<DialogCheckboxProps>;

View File

@@ -1,7 +1,9 @@
import { ReactNode, VFC } from 'react';
import { ReactNode, FC } from 'react';
import { CommonUIModule } from '../webpack';
import { ItemProps } from './Item';
import { createPropListRegex } from '../utils';
import type { ContextMenuPositionOptions } from './Menu';
export interface SingleDropdownOption {
data: any;
@@ -26,7 +28,7 @@ export interface DropdownProps {
onMenuWillOpen?(showMenu: () => void): void;
onMenuOpened?(): void;
onChange?(data: SingleDropdownOption): void;
contextMenuPositionOptions?: any;
contextMenuPositionOptions?: ContextMenuPositionOptions;
menuLabel?: string;
strDefaultLabel?: string;
renderButtonValue?(element: ReactNode): ReactNode;
@@ -35,10 +37,13 @@ export interface DropdownProps {
export const Dropdown = Object.values(CommonUIModule).find(
(mod: any) => mod?.prototype?.SetSelectedOption && mod?.prototype?.BuildMenu,
) as VFC<DropdownProps>;
) as FC<DropdownProps>;
export interface DropdownItemProps extends DropdownProps, ItemProps {}
export const DropdownItem = Object.values(CommonUIModule).find((mod: any) =>
mod?.toString()?.includes('"dropDownControlRef","description"'),
) as VFC<DropdownItemProps>;
const dropdownItemRegex = createPropListRegex(["dropDownControlRef", "description"], false);
export const DropdownItemInternal = Object.values(CommonUIModule).find((mod: any) =>
mod?.toString && dropdownItemRegex.test(mod.toString()),
) as FC<DropdownItemProps>;
export const DropdownItem = ((args: DropdownItemProps) => <DropdownItemInternal childrenContainerWidth="min" {...args}/>) as FC<DropdownItemProps>;

View File

@@ -0,0 +1,6 @@
import { FC, PropsWithChildren } from "react";
import { findModuleExport } from "../webpack";
export const ErrorBoundary = findModuleExport(
(e) => e.InstallErrorReportingStore && e?.prototype?.Reset && e?.prototype?.componentDidCatch,
) as FC<PropsWithChildren>; // Actually a class but @types/react is broken lol

View File

@@ -1,9 +1,10 @@
import { FC, ReactNode, RefAttributes } from 'react';
import { findModuleChild } from '../webpack';
import { Export, findModuleExport } from '../webpack';
import { FooterLegendProps } from './FooterLegend';
export interface FieldProps extends FooterLegendProps {
children?: ReactNode;
label?: ReactNode;
bottomSeparator?: 'standard' | 'thick' | 'none';
description?: ReactNode;
@@ -23,9 +24,7 @@ export interface FieldProps extends FooterLegendProps {
onClick?: (e: CustomEvent | MouseEvent) => void;
}
export const Field = findModuleChild((m) => {
if (typeof m !== 'object') return undefined;
for (let prop in m) {
if (m[prop]?.render?.toString().includes('"shift-children-below"')) return m[prop];
}
}) as FC<FieldProps & RefAttributes<HTMLDivElement>>;
// new || old
export const Field = findModuleExport((e: Export) => (e?.toString()?.includes('().Field') && e?.toString()?.includes('"shift-children-below"')) || e?.render?.toString()?.includes('"shift-children-below"')) as FC<
FieldProps & RefAttributes<HTMLDivElement>
>;

View File

@@ -0,0 +1,15 @@
import { ElementType, FC, ReactNode } from 'react';
import { Export, findModuleExport } from '../webpack';
export interface FocusRingProps {
className?: string;
rootClassName?: string;
render?: ElementType;
children?: ReactNode;
NavigationManager?: any;
}
export const FocusRing = findModuleExport((e: Export) =>
e?.toString?.()?.includes('.GetShowDebugFocusRing())'),
) as FC<FocusRingProps>;

View File

@@ -0,0 +1,21 @@
import { HTMLAttributes, ReactNode, RefAttributes, FC } from 'react';
import { Export, findModuleExport } from '../webpack';
import { FooterLegendProps } from './FooterLegend';
import { createPropListRegex } from '../utils';
export interface FocusableProps extends HTMLAttributes<HTMLDivElement>, FooterLegendProps {
children: ReactNode;
'flow-children'?: string;
focusClassName?: string;
focusWithinClassName?: string;
noFocusRing?: boolean;
onActivate?: (e: CustomEvent) => void;
onCancel?: (e: CustomEvent) => void;
}
const focusableRegex = createPropListRegex(["flow-children", "onActivate", "onCancel", "focusClassName", "focusWithinClassName"]);
export const Focusable = findModuleExport((e: Export) =>
(typeof e == 'function' && e?.toString && focusableRegex.test(e.toString())) || (e?.render?.toString && focusableRegex.test(e.render.toString()))
) as FC<FocusableProps & RefAttributes<HTMLDivElement>>;

View File

@@ -0,0 +1,117 @@
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 enum NavEntryPositionPreferences {
/**
* Always give focus to the first child element.
*/
FIRST,
/**
* Always give focus to the last child element.
*/
LAST,
/**
* Give focus to the child element that would maintain the flow in the X axis.
*
* Imagine you have a calculator window with 9 standard buttons.
* You have 3 rows of buttons, with 3 buttons per row.
* If you select button with number 8 and navigate down, the buttons
* will be navigated in the following order 8->5->3.
* The flow is maintained for the X axis while you're navigating the Y axis.
*/
MAINTAIN_X,
/**
* Give focus to the child element that would maintain the flow in the Y axis.
*
* Imagine you have a calculator window with 9 standard buttons.
* You have 3 columns of buttons, with 3 buttons per column.
* If you select button with number 4 and navigate right, the buttons
* will be navigated in the following order 4->5->6.
* The flow is maintained for the Y axis while you're navigating the X axis.
*/
MAINTAIN_Y,
/**
* Give focus to the first child element with `preferredFocus == true` prop.
*/
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 {
/**
* Navigation entry strategy to be used when gaining focus during navigation.
*
* This is meant to be used on a parent container that has children. Once the
* container (e.g. Focusable) is navigated to and has children in it, the children
* is then navigated to (focused) using the provided strategy.
*
* If no strategy is provided, it seems that the `NavEntryPositionPreferences.FIRST`
* is used initialy, but for the next time the parent remembers previously focused
* child and focused back on it instead.
*/
navEntryPreferPosition?: NavEntryPositionPreferences;
/**
* Mark the element as the preferred child (to be focused) whenever the parent uses the
* `NavEntryPositionPreferences.PREFERRED_CHILD` navigation strategy.
*/
preferredFocus?: boolean;
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

@@ -3,9 +3,11 @@ import { ReactNode } from 'react';
export interface ItemProps {
label?: ReactNode;
description?: ReactNode;
children?: ReactNode;
layout?: 'below' | 'inline';
icon?: ReactNode;
bottomSeparator?: 'standard' | 'thick' | 'none';
childrenContainerWidth?: 'min' | 'max' | 'fixed'; // Does not work with layout==='below'
indentLevel?: number;
tooltip?: string;
highlightOnFocus?: boolean;

20
src/components/Marquee.ts Normal file
View File

@@ -0,0 +1,20 @@
import { CSSProperties, FC } from 'react';
import { Export, findModuleExport } 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> = findModuleExport(
(e: Export) => e?.toString && e.toString().includes('.Marquee') && e.toString().includes('--fade-length'),
);

175
src/components/Menu.ts Executable file
View File

@@ -0,0 +1,175 @@
import { FC, ReactNode } from 'react';
import { Export, findModuleByExport, findModuleDetailsByExport, findModuleExport } from '../webpack';
import { FooterLegendProps } from './FooterLegend';
interface PopupCreationOptions {
/**
* Initially hidden, make it appear with {@link ContextMenuInstance.Show}.
*/
bCreateHidden?: boolean;
bModal?: boolean;
/**
* Document title.
*/
title?: string;
}
// Separate interface, since one of webpack module exports uses this exact object,
// so maybe it could be reused elsewhere.
interface MonitorOptions {
targetMonitor: {
flMonitorScale: number;
nScreenLeft: number;
nScreenTop: number;
nScreenWidth: number;
nScreenHeight: number;
};
flGamepadScale: number;
}
export interface ContextMenuPositionOptions extends PopupCreationOptions, Partial<MonitorOptions> {
/**
* When {@link bForcePopup} is true, makes the window appear above everything else.
*/
bAlwaysOnTop?: boolean;
/**
* Disables the mouse overlay, granting the ability to click anywhere while
* the menu's active.
*/
bDisableMouseOverlay?: boolean;
/**
* Disables the {@link bPreferPopTop} behavior.
*/
bDisablePopTop?: boolean;
bFitToWindow?: boolean;
/**
* Forces the menu to open in a separate window instead of inside the parent.
*/
bForcePopup?: boolean;
/**
* Like {@link bMatchWidth}, but don't shrink below the menu's minimum width.
*/
bGrowToElementWidth?: boolean;
/**
* Match the parent's exact height.
*/
bMatchHeight?: boolean;
/**
* Match the parent's exact width.
*/
bMatchWidth?: boolean;
bNoFocusWhenShown?: boolean;
/**
* Makes the menu **invisible**, instead of getting removed from the DOM.
*/
bRetainOnHide?: boolean;
bScreenCoordinates?: boolean;
/**
* Set to `true` to not account for the parent's width.
*/
bOverlapHorizontal?: boolean;
/**
* Set to `true` to not account for the parent's height.
*/
bOverlapVertical?: boolean;
/**
* Set to `true` to make the entire menu try to appear on the left side from
* the parent.
*/
bPreferPopLeft?: boolean;
/**
* Set to `true` to make the entire menu try to appear above the parent.
*/
bPreferPopTop?: boolean;
bShiftToFitWindow?: boolean;
// different window creation flags (StandaloneContextMenu vs PopupContextMenu)
bStandalone?: boolean;
/**
* Class name **replacement** for the container element, i.e. it replaces the
* default class.
*/
strClassName?: string;
}
interface ContextMenuInstance {
Hide(): void;
Show(): void;
}
export const showContextMenu: (
children: ReactNode,
parent?: EventTarget,
options?: ContextMenuPositionOptions,
) => ContextMenuInstance = findModuleExport(
(e: Export) =>
typeof e === 'function' &&
e.toString().includes('GetContextMenuManagerFromWindow(') &&
e.toString().includes('.CreateContextMenuInstance('),
);
export interface MenuProps extends FooterLegendProps {
label: string;
onCancel?(): void;
cancelText?: string;
children?: ReactNode;
}
const MenuModule = findModuleDetailsByExport((e: Export) => e?.render?.toString()?.includes('bPlayAudio:') || (e?.prototype?.OnOKButton && e?.prototype?.OnMouseEnter));
export const Menu: FC<MenuProps> =
findModuleExport((e: Export) => e?.prototype?.HideIfSubmenu && e?.prototype?.HideMenu) || // Legacy Menu
(Object.values(MenuModule?.[0] ?? {}).find((e) => e?.toString()?.includes?.(`useId`) && e?.toString()?.includes?.(`labelId`)) as FC<MenuProps>); // New Menu 6/15/2025
export interface MenuGroupProps {
label: string;
disabled?: boolean;
children?: ReactNode;
}
const MenuGoupModule = findModuleByExport(e => e?.prototype?.Focus && e?.prototype?.OnOKButton && e?.prototype?.render?.toString().includes?.(`"emphasis"==this.props.tone`));
export const MenuGroup: FC<MenuGroupProps> = MenuGoupModule && Object.values(MenuGoupModule).find((e: Export) => typeof e == "function" && e?.toString?.()?.includes("bInGamepadUI:"));
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> = MenuModule?.[1];
export const MenuSeparator: FC = findModuleExport(
(e: Export) => typeof e === 'function' && /className:.+?\.ContextMenuSeparator/.test(e.toString()),
);
/*
all().map(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]}
}).find(x => x)
*/

121
src/components/Modal.ts Executable file
View File

@@ -0,0 +1,121 @@
import { FC, ReactNode } from 'react';
import { findSP } from '../utils';
import { Export, findModule, findModuleDetailsByExport, findModuleExport } from '../webpack';
// All of the popout options + strTitle are related. Proper usage is not yet known...
export interface ShowModalProps {
browserContext?: unknown;
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,
) => ShowModalResult = findModuleExport(
(e: Export) =>
typeof e === 'function' && e.toString().includes('props.bDisableBackgroundDismiss') && !e?.prototype?.Cancel,
);
export const showModal = (
modal: ReactNode,
parent?: EventTarget,
props: ShowModalProps = {
strTitle: 'Decky Dialog',
bHideMainWindowForPopouts: false,
},
): ShowModalResult => {
return showModalRaw(modal, parent || findSP() || window, props.strTitle, props, undefined, {
bHideActions: props.bHideActionIcons,
});
};
export interface ModalRootProps {
children?: ReactNode;
onCancel?(): void;
closeModal?(): void;
onOK?(): void;
onEscKeypress?(): void;
className?: string;
modalClassName?: string;
bAllowFullSize?: boolean;
bDestructiveWarning?: boolean;
bDisableBackgroundDismiss?: boolean;
bHideCloseIcon?: boolean;
bOKDisabled?: boolean;
bCancelDisabled?: boolean;
}
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 = findModuleExport(
(e: Export) => e?.toString()?.includes('bUpdateDisabled') && e?.toString()?.includes('closeModal') && e?.toString()?.includes('onGamepadCancel'),
) as FC<ConfirmModalProps>;
export const ModalRoot =
// new
findModuleExport((e: Export) => typeof e === 'function' && e.toString().includes('Either closeModal or onCancel should be passed to GenericDialog. Classes: ')) ||
// old
Object.values(
findModule((m: any) => {
if (typeof m !== 'object') return false;
for (let prop in m) {
if (m[prop]?.m_mapModalManager && Object.values(m)?.find((x: any) => x?.type)) {
return true;
}
}
return false;
}) || {},
)?.find((x: any) => x?.type?.toString?.()?.includes('((function(){')) as FC<ModalRootProps>;
interface SimpleModalProps {
active?: boolean;
children: ReactNode;
}
const [ModalModule, _ModalPosition] = findModuleDetailsByExport((e: Export) => e?.toString().includes('.ModalPosition'), 5)
const ModalModuleProps = ModalModule ? Object.values(ModalModule) : [];
export const SimpleModal = ModalModuleProps.find((prop) => {
const string = prop?.toString();
return string?.includes('.ShowPortalModal()') && string?.includes('.OnElementReadyCallbacks.Register(');
}) as FC<SimpleModalProps>;
export const ModalPosition = _ModalPosition as FC<SimpleModalProps>;

26
src/components/Panel.ts Normal file
View File

@@ -0,0 +1,26 @@
import { FC, ReactNode } from 'react';
import { Export, findModuleDetailsByExport } from '../webpack';
// TODO where did this go?
// export const Panel: FC<{ children?: ReactNode; }> = findModuleExport((e: Export) => {
// if (typeof mod !== 'object' || !mod.__esModule) return undefined;
// return mod.Panel;
// });
export interface PanelSectionProps {
title?: string;
spinner?: boolean;
children?: ReactNode;
}
const [mod, panelSection] = findModuleDetailsByExport((e: Export) => e.toString()?.includes('.PanelSection'));
export const PanelSection = panelSection as FC<PanelSectionProps>;
export interface PanelSectionRowProps {
children?: ReactNode;
}
export const PanelSectionRow = Object.values(mod).filter(
(exp: any) => !exp?.toString?.()?.includes('.PanelSection'),
)[0] as FC<PanelSectionRowProps>;

View File

@@ -0,0 +1,38 @@
import { ReactNode, FC } from 'react';
import { Export, findModuleExport } from '../webpack';
import { ItemProps } from './Item';
import { createPropListRegex } from '../utils';
export interface ProgressBarItemProps extends ItemProps {
indeterminate?: boolean;
nTransitionSec?: number;
nProgress?: number;
focusable?: boolean;
}
export interface ProgressBarProps {
indeterminate?: boolean;
nTransitionSec?: number;
nProgress?: number;
focusable?: boolean;
}
export interface ProgressBarWithInfoProps extends ProgressBarItemProps {
sTimeRemaining?: ReactNode;
sOperationText?: ReactNode;
}
export const ProgressBar = findModuleExport((e: Export) =>
e?.toString?.()?.includes('.ProgressBar,"standard"=='),
) as FC<ProgressBarProps>;
export const ProgressBarWithInfo = findModuleExport((e: Export) =>
// new || old
e?.toString?.()?.includes('.ProgressBarFieldStatus,children') || e?.toString?.()?.includes('.ProgressBarFieldStatus},'),
) as FC<ProgressBarWithInfoProps>;
const progressBarItemRegex = createPropListRegex(["indeterminate", "nTransitionSec", "nProgress"]);
export const ProgressBarItem = findModuleExport((e: Export) =>
e?.toString && progressBarItemRegex.test(e.toString()),
) as FC<ProgressBarItemProps>;

16
src/components/Scroll.ts Normal file
View File

@@ -0,0 +1,16 @@
import { FC, ReactNode } from 'react';
import { Export, findModuleByExport, findModuleExport } from '../webpack';
const ScrollingModule = findModuleByExport((e: Export) => e?.render?.toString?.().includes('{case"x":'));
const ScrollingModuleProps = ScrollingModule ? Object.values(ScrollingModule) : [];
export const ScrollPanel = ScrollingModuleProps.find((prop: any) =>
prop?.render?.toString?.().includes('{case"x":'),
) as FC<{ children?: ReactNode }>;
export const ScrollPanelGroup: FC<{ children?: ReactNode }> = findModuleExport((e: Export) =>
// new || old
e?.render?.toString().includes('.FocusVisibleChild(),[])') || e?.render?.toString().includes('.FocusVisibleChild()),[])'),
);

View File

@@ -1,6 +1,7 @@
import { ReactNode, VFC } from 'react';
import { ReactNode, FC } from 'react';
import { Module, findModuleChild } from '../webpack';
import { Export, findModuleExport } from '../webpack';
import { createPropListRegex } from '../utils';
export interface SidebarNavigationPage {
title: ReactNode;
@@ -15,7 +16,7 @@ export interface SidebarNavigationPage {
}
export interface SidebarNavigationProps {
title?: string;
title?: ReactNode;
pages: (SidebarNavigationPage | 'separator')[];
showTitle?: boolean;
disableRouteReporting?: boolean;
@@ -23,11 +24,7 @@ export interface SidebarNavigationProps {
onPageRequested?: (page: string) => void;
}
export const SidebarNavigation = findModuleChild((mod: Module) => {
for (let prop in mod) {
if (mod[prop]?.toString()?.includes('"disableRouteReporting"')) {
return mod[prop];
}
}
return null;
}) as VFC<SidebarNavigationProps>;
const sidebarNavigationRegex = createPropListRegex(["pages", "fnSetNavigateToPage", "disableRouteReporting"]);
export const SidebarNavigation = findModuleExport((e: Export) =>
e?.toString && sidebarNavigationRegex.test(e.toString()),
) as FC<SidebarNavigationProps>;

View File

@@ -29,5 +29,6 @@ export interface SliderFieldProps extends ItemProps {
}
export const SliderField = Object.values(CommonUIModule).find((mod: any) =>
mod?.toString()?.includes('SliderField,fallback'),
// stable || beta as of oct 2 2024
mod?.toString?.()?.includes('SliderField,fallback') || mod?.toString?.()?.includes("SliderField\",")
) as FC<SliderFieldProps>;

8
src/components/Spinner.ts Executable file
View File

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

12
src/components/SteamSpinner.ts Executable file
View File

@@ -0,0 +1,12 @@
import { FC, ReactNode, SVGAttributes } from 'react';
import { Export, findModuleExport } from '../webpack';
interface SteamSpinnerProps {
children?: ReactNode;
background?: "transparent"; // defaults to black seemingly, but only "transparent" is checked against
}
export const SteamSpinner = findModuleExport(
(e: Export) => e?.toString?.()?.includes('Steam Spinner') && e?.toString?.()?.includes('src'),
) as FC<SVGAttributes<SVGElement> & SteamSpinnerProps>;

70
src/components/Tabs.ts Normal file
View File

@@ -0,0 +1,70 @@
import { FC, ReactNode } from 'react';
import { findModuleByExport } from '../webpack';
import { FooterLegendProps } from './FooterLegend';
/**
* 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;
}
const tabsModule = findModuleByExport(e => e?.toString?.()?.includes(".TabRowTabs") && e?.toString?.()?.includes("activeTab:"));
/**
* Tabs component as used in the library and media tabs. See {@link TabsProps}.
*/
export const Tabs = tabsModule && Object.values(tabsModule).find((e: any) => e?.type?.toString?.()?.includes("(function()")) as FC<TabsProps>;

View File

@@ -1,4 +1,4 @@
import { ChangeEventHandler, HTMLAttributes, ReactNode, VFC } from 'react';
import { ChangeEventHandler, HTMLAttributes, ReactNode, FC } from 'react';
import { CommonUIModule, Module } from '../webpack';
@@ -25,4 +25,4 @@ export interface TextFieldProps extends HTMLAttributes<HTMLInputElement> {
export const TextField = Object.values(CommonUIModule).find(
(mod: Module) => mod?.validateUrl && mod?.validateEmail,
) as VFC<TextFieldProps>;
) as FC<TextFieldProps>;

View File

@@ -10,5 +10,5 @@ export interface ToggleProps {
}
export const Toggle = Object.values(CommonUIModule).find((mod: any) =>
mod?.render?.toString()?.includes('.ToggleOff)'),
mod?.render?.toString?.()?.includes('.ToggleOff)'),
) as FC<ToggleProps>;

View File

@@ -11,5 +11,6 @@ export interface ToggleFieldProps extends ItemProps {
}
export const ToggleField = Object.values(CommonUIModule).find((mod: any) =>
mod?.render?.toString()?.includes('ToggleField,fallback'),
// stable || beta as of oct 2 2024
mod?.render?.toString?.()?.includes('ToggleField,fallback') || mod?.render?.toString?.()?.includes("ToggleField\",")
) as FC<ToggleFieldProps>;

26
src/components/index.ts Executable file
View File

@@ -0,0 +1,26 @@
export * from './Button';
export * from './ButtonItem';
export * from './Carousel';
export * from './ControlsList';
export * from './Dialog';
export * from './DialogCheckbox';
export * from './Dropdown';
export * from './ErrorBoundary';
export * from './Field';
export * from './Focusable';
export * from './FocusRing';
export * from './FooterLegend';
export * from './Marquee';
export * from './Menu';
export * from './Modal';
export * from './Panel';
export * from './ProgressBar';
export * from './SidebarNavigation';
export * from './SliderField';
export * from './Spinner';
export * from './SteamSpinner';
export * from './Tabs';
export * from './TextField';
export * from './Toggle';
export * from './ToggleField';
export * from './Scroll';

View File

@@ -1,6 +1,7 @@
import { CSSProperties, FC, useState } from 'react';
import { ConfirmModal, SliderField, gamepadSliderClasses } from '../deck-components';
import { ConfirmModal, SliderField } from '../components';
import { gamepadSliderClasses } from '../utils/static-classes';
interface ColorPickerModalProps {
closeModal: () => void;

View File

@@ -1,6 +1,6 @@
import { Fragment, JSXElementConstructor, ReactElement, ReactNode, useEffect, useState } from 'react';
import { Field, FieldProps, Focusable, GamepadButton } from '../deck-components';
import { Field, FieldProps, Focusable, GamepadButton } from '../components';
/**
* A ReorderableList entry of type <T>.
@@ -35,12 +35,12 @@ export type ReorderableListProps<T> = {
export function ReorderableList<T>(props: ReorderableListProps<T>) {
if (props.animate === undefined) props.animate = true;
const [entryList, setEntryList] = useState<ReorderableEntry<T>[]>(
props.entries.sort((a: ReorderableEntry<T>, b: ReorderableEntry<T>) => a.position - b.position),
[...props.entries].sort((a: ReorderableEntry<T>, b: ReorderableEntry<T>) => a.position - b.position),
);
const [reorderEnabled, setReorderEnabled] = useState<boolean>(false);
useEffect(() => {
setEntryList(props.entries.sort((a: ReorderableEntry<T>, b: ReorderableEntry<T>) => a.position - b.position));
setEntryList([...props.entries].sort((a: ReorderableEntry<T>, b: ReorderableEntry<T>) => a.position - b.position));
}, [props.entries]);
function toggleReorderEnabled(): void {
@@ -108,7 +108,7 @@ export type ReorderableListEntryProps<T> = {
reorderEntryFunc: CallableFunction;
reorderEnabled: boolean;
animate: boolean;
children: ReactElement | null;
children: ReactElement<any> | null;
};
function ReorderableItem<T>(props: ReorderableListEntryProps<T>) {

View File

@@ -1,7 +1,7 @@
import { useEffect } from 'react';
import { FC, ImgHTMLAttributes, useState } from 'react';
import { Spinner } from '../deck-components';
import { Spinner } from '../components';
interface SuspensefulImageProps extends ImgHTMLAttributes<HTMLImageElement> {
suspenseWidth?: string | number;

View File

@@ -0,0 +1,65 @@
import { useEffect, useState } from 'react';
import { getGamepadNavigationTrees } from '../utils';
function getQuickAccessWindow(): Window | null {
const navTrees = getGamepadNavigationTrees();
return (
navTrees.find((tree: any) => tree?.id === 'QuickAccess-NA')?.m_Root?.m_element?.ownerDocument.defaultView ?? null
);
}
/**
* Returns state indicating the visibility of quick access menu.
*
* @deprecated moved to @decky/api
*
* @returns `true` if quick access menu is visible and `false` otherwise.
*
* @example
* import { FC, useEffect } from "react";
* import { useQuickAccessVisible } from "@decky/ui";
*
* export const PluginPanelView: FC<{}> = ({ }) => {
* 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 {
// By default we say that document is not hidden, unless we know otherwise.
// This would cover the cases when Valve breaks something and the quick access window
// cannot be accessed anymore - the plugins that use this would continue working somewhat.
const [isHidden, setIsHidden] = useState(getQuickAccessWindow()?.document.hidden ?? false);
useEffect(() => {
const quickAccessWindow = getQuickAccessWindow();
if (quickAccessWindow === null) {
console.error('Could not get window of QuickAccess menu!');
return;
}
const onVisibilityChange = () => setIsHidden(quickAccessWindow.document.hidden);
quickAccessWindow.addEventListener('visibilitychange', onVisibilityChange);
return () => {
quickAccessWindow.removeEventListener('visibilitychange', onVisibilityChange);
};
}, []);
return !isHidden;
}

View File

@@ -1,68 +0,0 @@
import { useEffect, useState } from 'react';
import { getGamepadNavigationTrees } from '../utils';
function getQuickAccessWindow(): Window | null {
const navTrees = getGamepadNavigationTrees();
return navTrees.find((tree: any) => tree?.id === 'QuickAccess-NA')?.m_Root?.m_element?.ownerDocument.defaultView ?? 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,16 +0,0 @@
import { FC } from 'react';
import { CommonUIModule } from '../webpack';
import { ItemProps } from './Item';
export interface ButtonItemProps extends ItemProps {
onClick?(e: MouseEvent): void;
disabled?: boolean;
}
export const ButtonItem =
CommonUIModule.ButtonField ||
(Object.values(CommonUIModule).find(
(mod: any) =>
mod?.render?.toString()?.includes('"highlightOnFocus","childrenContainerWidth"') ||
mod?.render?.toString()?.includes('childrenContainerWidth:"min"'),
) as FC<ButtonItemProps>);

View File

@@ -1,17 +0,0 @@
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

@@ -1,33 +0,0 @@
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

@@ -1,19 +0,0 @@
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,22 +0,0 @@
import { HTMLAttributes, ReactNode, RefAttributes, VFC } from 'react';
import { findModuleChild } from '../webpack';
import { FooterLegendProps } from './FooterLegend';
export interface FocusableProps extends HTMLAttributes<HTMLDivElement>, FooterLegendProps {
children: ReactNode;
'flow-children'?: string;
focusClassName?: string;
focusWithinClassName?: string;
noFocusRing?: boolean;
onActivate?: (e: CustomEvent) => void;
onCancel?: (e: CustomEvent) => void;
}
export const Focusable = findModuleChild((m) => {
if (typeof m !== 'object') return undefined;
for (let prop in m) {
if (m[prop]?.render?.toString()?.includes('["flow-children","onActivate","onCancel","focusClassName",'))
return m[prop];
}
}) as VFC<FocusableProps & RefAttributes<HTMLDivElement>>;

View File

@@ -1,67 +0,0 @@
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,26 +0,0 @@
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,84 +0,0 @@
import { FC, ReactNode } from 'react';
import { fakeRenderComponent } from '../utils';
import { findModuleChild } from '../webpack';
import { FooterLegendProps } from './FooterLegend';
export const showContextMenu: (children: ReactNode, parent?: EventTarget) => void = findModuleChild((m) => {
if (typeof m !== 'object') return undefined;
for (let prop in m) {
if (typeof m[prop] === 'function' && m[prop].toString().includes('stopPropagation))')) {
return m[prop];
}
}
});
export interface MenuProps extends FooterLegendProps {
label: string;
onCancel?(): void;
cancelText?: string;
children?: ReactNode;
}
export const Menu: FC<MenuProps> = findModuleChild((m) => {
if (typeof m !== 'object') return undefined;
for (let prop in m) {
if (m[prop]?.prototype?.HideIfSubmenu && m[prop]?.prototype?.HideMenu) {
return m[prop];
}
}
});
export interface MenuGroupProps {
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]({overview: {appid: 7}}))?.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) => {
if (typeof m !== 'object') return undefined;
for (let prop in m) {
if (
m[prop]?.render?.toString()?.includes('bPlayAudio:') ||
(m[prop]?.prototype?.OnOKButton && m[prop]?.prototype?.OnMouseEnter)
) {
return m[prop];
}
}
});
/*
all().map(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]}
}).find(x => x)
*/

View File

@@ -1,169 +0,0 @@
import { FC, ReactNode } from 'react';
import { findSP } from '../utils';
import { findModule, findModuleChild } from '../webpack';
// All of the popout options + strTitle are related. Proper usage is not yet known...
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,
) => 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) => ShowModalResult) | void =
findModuleChild((m) => {
if (typeof m !== 'object') return undefined;
for (let prop in m) {
if (typeof m[prop] === 'function' && m[prop].toString().includes('bHideMainWindowForPopouts:!0')) {
return m[prop];
}
}
});
export const showModal = (
modal: ReactNode,
parent?: EventTarget,
props: ShowModalProps = {
strTitle: 'Decky Dialog',
bHideMainWindowForPopouts: false,
},
): 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 {
children?: ReactNode;
onCancel?(): void;
closeModal?(): void;
onOK?(): void;
onEscKeypress?(): void;
className?: string;
modalClassName?: string;
bAllowFullSize?: boolean;
bDestructiveWarning?: boolean;
bDisableBackgroundDismiss?: boolean;
bHideCloseIcon?: boolean;
bOKDisabled?: boolean;
bCancelDisabled?: boolean;
}
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;
for (let prop in m) {
if (!m[prop]?.prototype?.OK && m[prop]?.prototype?.Cancel && m[prop]?.prototype?.render) {
return m[prop];
}
}
}) as FC<ConfirmModalProps>;
// new as of december 2022 on beta
export const ModalRoot = (Object.values(
findModule((m: any) => {
if (typeof m !== 'object') return false;
for (let prop in m) {
if (m[prop]?.m_mapModalManager && Object.values(m)?.find((x: any) => x?.type)) {
return true;
}
}
return false;
}) || {},
)?.find((x: any) => x?.type?.toString()?.includes('((function(){')) ||
// before december 2022 beta
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>;
interface SimpleModalProps{
active?: boolean,
children: ReactNode
}
const ModalModule = findModule((mod) => {
if (typeof mod !== 'object' || !mod.__esModule) return undefined;
if (mod.SimpleModal && mod.ModalPosition) return mod;
})
export const SimpleModal = ModalModule.SimpleModal as FC<SimpleModalProps>
export const ModalPosition = ModalModule.ModalPosition as FC<SimpleModalProps>

View File

@@ -1,33 +0,0 @@
import { FC, ReactNode } from 'react';
import { findModuleChild } from '../webpack';
export const Panel: FC<{ children?: ReactNode; }> = findModuleChild((mod) => {
if (typeof mod !== 'object' || !mod.__esModule) return undefined;
return mod.Panel;
})
export interface PanelSectionProps {
title?: string;
spinner?: boolean;
children?: ReactNode;
}
const [panelSection, mod] = findModuleChild((mod: any) => {
for (let prop in mod) {
if (mod[prop]?.toString()?.includes('.PanelSection')) {
return [mod[prop], mod];
}
}
return null;
});
export const PanelSection = panelSection as FC<PanelSectionProps>;
export interface PanelSectionRowProps {
children?: ReactNode;
}
// New as of Feb 22 2023 Beta || Old
export const PanelSectionRow =
mod.PanelSectionRow ||
(Object.values(mod).filter((exp: any) => !exp?.toString()?.includes('.PanelSection'))[0] as FC<PanelSectionRowProps>);

View File

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

View File

@@ -1,186 +0,0 @@
import { sleep } from '../utils';
import { Module, findModuleChild } from '../webpack';
export enum SideMenu {
None,
Main,
QuickAccess,
}
export enum QuickAccessTab {
Notifications,
RemotePlayTogetherControls,
VoiceChat,
Friends,
Settings,
Perf,
Help,
Music,
Decky = 999,
}
export enum DisplayStatus {
Invalid = 0,
Launching = 1,
Uninstalling = 2,
Installing = 3,
Running = 4,
Validating = 5,
Updating = 6,
Downloading = 7,
Synchronizing = 8,
ReadyToInstall = 9,
ReadyToPreload = 10,
ReadyToLaunch = 11,
RegionRestricted = 12,
PresaleOnly = 13,
InvalidPlatform = 14,
PreloadComplete = 16,
BorrowerLocked = 17,
UpdatePaused = 18,
UpdateQueued = 19,
UpdateRequired = 20,
UpdateDisabled = 21,
DownloadPaused = 22,
DownloadQueued = 23,
DownloadRequired = 24,
DownloadDisabled = 25,
LicensePending = 26,
LicenseExpired = 27,
AvailForFree = 28,
AvailToBorrow = 29,
AvailGuestPass = 30,
Purchase = 31,
Unavailable = 32,
NotLaunchable = 33,
CloudError = 34,
CloudOutOfDate = 35,
Terminating = 36,
}
export type AppOverview = {
appid: string;
display_name: string;
display_status: DisplayStatus;
sort_as: string;
};
export interface MenuStore {
OpenSideMenu(sideMenu: SideMenu): void;
OpenQuickAccessMenu(quickAccessTab?: QuickAccessTab): void;
OpenMainMenu(): void;
}
export interface WindowRouter {
BrowserWindow: Window;
MenuStore: MenuStore;
Navigate(path: string): void;
NavigateToChat(): void;
NavigateToSteamWeb(url: string): void;
NavigateBack(): void;
}
export interface WindowStore {
GamepadUIMainWindowInstance?: WindowRouter; // Current
SteamUIWindows: WindowRouter[];
OverlayWindows: WindowRouter[]; // Used by desktop GamepadUI
}
export interface Router {
WindowStore?: WindowStore;
CloseSideMenus(): void;
Navigate(path: string): void;
NavigateToAppProperties(): void;
NavigateToExternalWeb(url: string): void;
NavigateToInvites(): void;
NavigateToChat(): void;
NavigateToLibraryTab(): void;
NavigateToLayoutPreview(e: unknown): void;
OpenPowerMenu(unknown?: any): void;
get RunningApps(): AppOverview[];
get MainRunningApp(): AppOverview | undefined;
}
export const Router = findModuleChild((m: Module) => {
if (typeof m !== 'object') return undefined;
for (let prop in m) {
if (m[prop]?.Navigate && m[prop]?.NavigationManager) return m[prop];
}
}) as Router;
export interface Navigation {
Navigate(path: string): void;
NavigateBack(): void;
NavigateToAppProperties(): void;
NavigateToExternalWeb(url: string): void;
NavigateToInvites(): void;
NavigateToChat(): void;
NavigateToLibraryTab(): void;
NavigateToLayoutPreview(e: unknown): void;
NavigateToSteamWeb(url: string): void;
OpenSideMenu(sideMenu: SideMenu): void;
OpenQuickAccessMenu(quickAccessTab?: QuickAccessTab): void;
OpenMainMenu(): void;
OpenPowerMenu(unknown?: any): void;
CloseSideMenus(): void;
}
export let Navigation = {} as Navigation;
try {
(async () => {
let InternalNavigators: any = {};
if (!Router.NavigateToAppProperties || (Router as unknown as any).deckyShim) {
function initInternalNavigators() {
try {
InternalNavigators = findModuleChild((m: any) => {
if (typeof m !== 'object') return undefined;
for (let prop in m) {
if (m[prop]?.GetNavigator) {
return m[prop];
}
}
})?.GetNavigator();
} catch (e) {
console.error('[DFL:Router]: Failed to init internal navigators, trying again');
}
}
initInternalNavigators();
while (!InternalNavigators?.AppProperties) {
console.log('[DFL:Router]: Trying to init internal navigators again');
await sleep(100);
initInternalNavigators();
}
}
const newNavigation = {
Navigate: Router.Navigate?.bind(Router),
NavigateBack: Router.WindowStore?.GamepadUIMainWindowInstance?.NavigateBack?.bind(
Router.WindowStore.GamepadUIMainWindowInstance,
),
NavigateToAppProperties: InternalNavigators?.AppProperties || Router.NavigateToAppProperties?.bind(Router),
NavigateToExternalWeb: InternalNavigators?.ExternalWeb || Router.NavigateToExternalWeb?.bind(Router),
NavigateToInvites: InternalNavigators?.Invites || Router.NavigateToInvites?.bind(Router),
NavigateToChat: Router.NavigateToChat?.bind(Router),
NavigateToLibraryTab: InternalNavigators?.LibraryTab || Router.NavigateToLibraryTab?.bind(Router),
NavigateToLayoutPreview: Router.NavigateToLayoutPreview?.bind(Router),
NavigateToSteamWeb: Router.WindowStore?.GamepadUIMainWindowInstance?.NavigateToSteamWeb?.bind(
Router.WindowStore.GamepadUIMainWindowInstance,
),
OpenSideMenu: Router.WindowStore?.GamepadUIMainWindowInstance?.MenuStore.OpenSideMenu?.bind(
Router.WindowStore.GamepadUIMainWindowInstance.MenuStore,
),
OpenQuickAccessMenu: Router.WindowStore?.GamepadUIMainWindowInstance?.MenuStore.OpenQuickAccessMenu?.bind(
Router.WindowStore.GamepadUIMainWindowInstance.MenuStore,
),
OpenMainMenu: Router.WindowStore?.GamepadUIMainWindowInstance?.MenuStore.OpenMainMenu?.bind(
Router.WindowStore.GamepadUIMainWindowInstance.MenuStore,
),
CloseSideMenus: Router.CloseSideMenus?.bind(Router),
OpenPowerMenu: Router.OpenPowerMenu?.bind(Router),
} as Navigation;
Object.assign(Navigation, newNavigation);
})();
} catch (e) {
console.error('[DFL:Router]: Error initializing Navigation interface', e);
}

View File

@@ -1,14 +0,0 @@
import { FC, ReactNode } from "react";
import { findModuleChild, findModule } from "../webpack";
const ScrollingModule = findModule((mod) => {
if (typeof mod !== 'object' || !mod.__esModule) return undefined;
if (mod.ScrollPanel) return mod;
});
export const ScrollPanel: FC<{ children?: ReactNode; }> = ScrollingModule.ScrollPanel;
export const ScrollPanelGroup: FC<{ children?: ReactNode; }> = findModuleChild((mod) => {
if (typeof mod !== 'object' || !mod.__esModule) return undefined;
return mod.ScrollPanelGroup;
})

View File

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

View File

@@ -1,317 +0,0 @@
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 type LogoPinPositions = 'BottomLeft' | 'UpperLeft' | 'CenterCenter' | 'UpperCenter' | 'BottomCenter';
export interface LogoPosition {
pinnedPosition: LogoPinPositions;
nWidthPct: number;
nHeightPct: number;
};
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[];
libraryAssets?: {
logoPosition?: LogoPosition;
};
}
export interface SteamAppOverview {
display_name: string;
gameid: string;
appid: number;
icon_hash: string;
third_party_mod?: boolean;
selected_clientid?: string;
BIsModOrShortcut: () => boolean;
BIsShortcut: () => boolean;
}

View File

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

View File

@@ -1,134 +0,0 @@
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,82 +0,0 @@
export * from './Button';
export * from './ButtonItem';
export * from './Carousel';
export * from './ControlsList';
export * from './Dialog';
export * from './DialogCheckbox';
export * from './Dropdown';
export * from './Field';
export * from './Focusable';
export * from './FocusRing';
export * from './FooterLegend';
export * from './Marquee';
export * from './Menu';
export * from './Modal';
export * from './Panel';
export * from './ProgressBar';
export * from './Router';
export * from './SidebarNavigation';
export * from './SliderField';
export * from './Spinner';
export * from './static-classes';
export * from './SteamSpinner';
export * from './Tabs';
export * from './TextField';
export * from './Toggle';
export * from './ToggleField';
export * from './SteamClient';
export * from './Scroll';
import { AppDetails, LogoPosition, SteamAppOverview, SteamClient } from './SteamClient';
declare global {
var SteamClient: SteamClient;
interface Window {
LocalizationManager: {
m_mapTokens: Map<string, string>;
m_mapFallbackTokens: Map<string, string>;
m_rgLocalesToUse: string[];
};
App: {
m_CurrentUser: {
bIsLimited: boolean;
bIsOfflineMode: boolean;
bSupportAlertActive: boolean;
bCanInviteFriends: boolean;
NotificationCounts: {
comments: number;
inventory_items: number;
invites: number;
gifts: number;
offline_messages: number;
trade_offers: number;
async_game_updates: number;
moderator_messages: number;
help_request_replies: number;
};
strAccountBalance: string;
strAccountName: string;
strSteamID: string;
};
};
appStore: {
GetAppOverviewByAppID: (appId: number) => SteamAppOverview | null;
GetCustomVerticalCapsuleURLs: (app: SteamAppOverview) => string[];
GetCustomLandcapeImageURLs: (app: SteamAppOverview) => string[];
GetCustomHeroImageURLs: (app: SteamAppOverview) => string[];
GetCustomLogoImageURLs: (app: SteamAppOverview) => string[];
GetLandscapeImageURLForApp: (app: SteamAppOverview) => string;
GetVerticalCapsuleURLForApp: (app: SteamAppOverview) => string;
GetCachedLandscapeImageURLForApp: (app: SteamAppOverview) => string;
GetCachedVerticalImageURLForApp: (app: SteamAppOverview) => string;
GetPregeneratedVerticalCapsuleForApp: (app: SteamAppOverview) => string;
GetIconURLForApp: (app: SteamAppOverview) => string;
};
appDetailsStore: {
GetAppDetails: (appId: number) => AppDetails | null;
GetCustomLogoPosition: (app: SteamAppOverview) => LogoPosition | null;
SaveCustomLogoPosition: (app: SteamAppOverview, logoPositions: LogoPosition) => any;
};
}
}

View File

@@ -1,544 +0,0 @@
import { findModule } from '../webpack';
type QuickAccessMenuClasses = Record<
| 'ActiveTab'
| 'AllTabContents'
| 'BatteryDetailsLabels'
| 'BatteryIcon'
| 'BatteryPercentageLabel'
| 'BatteryProjectedLabel'
| 'BatteryProjectedValue'
| 'BatterySectionContainer'
| 'Blocked'
| 'ComingSoon'
| 'Container'
| 'ContentTransition'
| 'Down'
| 'EmptyNotifications'
| 'Enter'
| 'EnterActive'
| 'Exit'
| 'ExitActive'
| 'FooterBoxShadow'
| 'FriendsListTabPanel'
| 'FriendsTitle'
| 'FullHeight'
| 'HeaderAndFooterVisible'
| 'HeaderContainer'
| 'ItemFocusAnim-darkGrey'
| 'ItemFocusAnim-darkerGrey'
| 'ItemFocusAnim-darkerGrey-nocolor'
| 'ItemFocusAnim-green'
| 'ItemFocusAnim-grey'
| 'ItemFocusAnimBorder-darkGrey'
| 'KeyboardButton'
| 'Label'
| 'LowBattery'
| 'LowBatteryGauge'
| 'Menu'
| 'Open'
| 'PanelExitAnchor'
| 'PanelOuterNav'
| 'PanelSection'
| 'PanelSectionRow'
| 'PanelSectionTitle'
| 'QuickAccessMenu'
| 'ReallyLow'
| 'Remaining'
| 'Selected'
| 'Tab'
| 'TabContentColumn'
| 'TabGroupPanel'
| 'TabPanelHidden'
| 'Tabs'
| 'Text'
| 'Title'
| 'TransitionMenuDelay'
| 'Up'
| 'ViewPlaceholder'
| 'VoiceTab'
| 'duration-app-launch'
| 'focusAnimation'
| 'hoverAnimation',
string
>;
type ScrollPanelClasses = Record<'ScrollBoth' | 'ScrollPanel' | 'ScrollX' | 'ScrollY', string>;
type GamepadDialogClasses = Record<
| 'duration-app-launch'
| 'GamepadDialogContent'
| 'GamepadDialogContent_InnerWidth'
| 'Field'
| 'Button'
| 'NoMinWidth'
| 'ActiveAndUnfocused'
| 'StandaloneFieldSeparator'
| 'StandardPadding'
| 'CompactPadding'
| 'WithDescription'
| 'WithBottomSeparatorStandard'
| 'WithBottomSeparatorThick'
| 'HighlightOnFocus'
| 'ItemFocusAnim-darkerGrey'
| 'ItemFocusAnim-darkGrey'
| 'WithBottomSeparator'
| 'Disabled'
| 'Clickable'
| 'FieldClickTarget'
| 'FieldChildren'
| 'FieldLeadIcon'
| 'FieldLabelRow'
| 'VerticalAlignCenter'
| 'InlineWrapShiftsChildrenBelow'
| 'ExtraPaddingOnChildrenBelow'
| 'ChildrenWidthFixed'
| 'ChildrenWidthGrow'
| 'WithFirstRow'
| 'WithChildrenBelow'
| 'FieldLabel'
| 'FieldLabelValue'
| 'FieldDescription'
| 'ModalPosition'
| 'WithStandardPadding'
| 'slideInAnimation'
| 'BasicTextInput'
| 'Toggle'
| 'ToggleRail'
| 'On'
| 'ToggleSwitch'
| 'LabelFieldValue'
| 'DropDownControlButtonContents'
| 'Spacer'
| 'ControlsListOuterPanel'
| 'StandardSpacing'
| 'ExtraSpacing'
| 'AlignRight'
| 'AlignLeft'
| 'AlignCenter'
| 'ControlsListChild'
| 'QuickAccess-Menu'
| 'BigButtons'
| 'BottomButtons'
| 'ItemFocusAnim-darkerGrey-nocolor'
| 'ItemFocusAnim-grey'
| 'ItemFocusAnimBorder-darkGrey'
| 'ItemFocusAnim-green'
| 'focusAnimation'
| 'hoverAnimation',
string
>;
type QuickAccessControlsClasses = Record<
| 'duration-app-launch'
| 'PanelSection'
| 'PanelSectionTitle'
| 'Text'
| 'PanelSectionRow'
| 'Label'
| 'ComingSoon'
| 'LowBattery'
| 'ReallyLow'
| 'LowBatteryGauge'
| 'Remaining'
| 'EmptyNotifications'
| 'BatterySectionContainer'
| 'BatteryIcon'
| 'BatteryPercentageLabel'
| 'BatteryDetailsLabels'
| 'BatteryProjectedValue'
| 'BatteryProjectedLabel'
| 'ItemFocusAnim-darkerGrey-nocolor'
| 'ItemFocusAnim-darkerGrey'
| 'ItemFocusAnim-darkGrey'
| 'ItemFocusAnim-grey'
| 'ItemFocusAnimBorder-darkGrey'
| 'ItemFocusAnim-green'
| 'focusAnimation'
| 'hoverAnimation',
string
>;
type UpdaterFieldClasses = Record<
| 'duration-app-launch'
| 'OOBEUpdateStatusContainer'
| 'UpdateScreen'
| 'UpdatePanel'
| 'CurrentStatus'
| 'TotalUpdateSize'
| 'ProgressInfoContainer'
| 'TimeRemaining'
| 'BatteryLowWarning'
| 'fadeInAnimation'
| 'ProgressStatus'
| 'UpdateStatusContainer'
| 'UpdaterFieldStatusSuccess'
| 'UpdaterFieldStatusApplying'
| 'TextContainer'
| 'ApplyingText'
| 'UpdateBytesRemaining'
| 'Label'
| 'Numerator'
| 'Separator'
| 'Denominator'
| 'PatchNotes'
| 'PostedTime'
| 'EventDetailTitle'
| 'EventDetailsSubTitle'
| 'EventDetailsBody'
| 'InsufficientBatteryText'
| 'UnsupportedHardwareWarning'
| 'Title'
| 'Text'
| 'Body'
| 'ItemFocusAnim-darkerGrey-nocolor'
| 'ItemFocusAnim-darkerGrey'
| 'ItemFocusAnim-darkGrey'
| 'ItemFocusAnim-grey'
| 'ItemFocusAnimBorder-darkGrey'
| 'ItemFocusAnim-green'
| 'focusAnimation'
| 'hoverAnimation',
string
>;
type PlaySectionClasses = Record<
| 'AchievementCountLabel'
| 'AchievementProgressRow'
| 'ActionSection'
| 'AppButtonsContainer'
| 'Arrow'
| 'AvatarAndPersona'
| 'BreakNarrow'
| 'BreakShort'
| 'BreakTall'
| 'BreakUltraWide'
| 'BreakWide'
| 'ClickablePlayBarItem'
| 'CloudStatusIcon'
| 'CloudStatusLabel'
| 'CloudStatusRow'
| 'CloudSyncProblem'
| 'CloudSynching'
| 'ComingSoon'
| 'Container'
| 'DetailsProgressBar'
| 'DetailsProgressContainer'
| 'DetailsSection'
| 'DetailsSectionExtra'
| 'DetailsSectionStatus'
| 'DotDotDot'
| 'DownloadPaused'
| 'DownloadProgressBar'
| 'Downloading'
| 'FavoriteButton'
| 'Favorited'
| 'GameInfoButton'
| 'GameStat'
| 'GameStatIcon'
| 'GameStatIconForced'
| 'GameStatRight'
| 'GameStatsSection'
| 'GamepadUIBreakNarrow'
| 'GamepadUIBreakShort'
| 'GamepadUIBreakWide'
| 'Glassy'
| 'HideWhenNarrow'
| 'Icon'
| 'Icons'
| 'InPage'
| 'InnerContainer'
| 'InvalidPlatform'
| 'ItemFocusAnim-darkGrey'
| 'ItemFocusAnim-darkerGrey'
| 'ItemFocusAnim-darkerGrey-nocolor'
| 'ItemFocusAnim-green'
| 'ItemFocusAnim-grey'
| 'ItemFocusAnimBorder-darkGrey'
| 'Label'
| 'LastPlayed'
| 'LastPlayedInfo'
| 'MenuActive'
| 'MenuButton'
| 'MiniAchievements'
| 'OfflineMode'
| 'OnlyDownloadBar'
| 'PermanentlyUnavailable'
| 'PlayBar'
| 'PlayBarCloudStatusContainer'
| 'PlayBarDetailLabel'
| 'PlayBarGameIcon'
| 'PlayBarGameName'
| 'PlayBarIconAndGame'
| 'PlayBarLabel'
| 'Playtime'
| 'PlaytimeIcon'
| 'PlaytimeIconForced'
| 'PortraitBar'
| 'Presale'
| 'RecentlyUpdated'
| 'RecentlyUpdatedIcon'
| 'RecentlyUpdatedLink'
| 'RecentlyUpdatedText'
| 'RightBreakNarrow'
| 'RightBreakUltraNarrow'
| 'RightBreakUltraWide'
| 'RightBreakWide'
| 'RightControls'
| 'Row'
| 'SharedLibrary'
| 'StatusAndStats'
| 'StatusNameContainer'
| 'StickyHeader'
| 'StickyHeaderShadow'
| 'SuperimposedGridItems'
| 'SyncAnim'
| 'Visible'
| 'duration-app-launch'
| 'favorited'
| 'focusAnimation'
| 'hoverAnimation',
string
>;
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
>;
type AppDetailsHeaderClasses = Record<
| '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
>;
type AppDetailsClasses = Record<
| '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
>;
type GamepadUIClasses = Record<
| 'duration-app-launch'
| 'TransitionMenuDelay'
| 'PanelSection'
| 'PanelSectionTitle'
| 'Text'
| 'PanelSectionRow'
| 'Label'
| 'ComingSoon'
| 'LowBattery'
| 'ReallyLow'
| 'LowBatteryGauge'
| 'Remaining'
| 'EmptyNotifications'
| 'BatterySectionContainer'
| 'BatteryIcon'
| 'BatteryPercentageLabel'
| 'BatteryDetailsLabels'
| 'BatteryProjectedValue'
| 'BatteryProjectedLabel'
| 'ViewPlaceholder'
| 'FullHeight'
| 'Title'
| 'Container'
| 'Open'
| 'QuickAccessMenu'
| 'HeaderContainer'
| 'Menu'
| 'HeaderAndFooterVisible'
| 'TabContentColumn'
| 'Tabs'
| 'Tab'
| 'Selected'
| 'ItemFocusAnim-darkerGrey'
| 'ItemFocusAnim-darkerGrey-nocolor'
| 'VoiceTab'
| 'ItemFocusAnim-green'
| 'Blocked'
| 'TabPanelHidden'
| 'FriendsTitle'
| 'FriendsListTabPanel'
| 'PanelOuterNav'
| 'PanelExitAnchor'
| 'TabGroupPanel'
| 'FooterBoxShadow'
| 'AllTabContents'
| 'ContentTransition'
| 'ActiveTab'
| 'Up'
| 'Enter'
| 'EnterActive'
| 'Exit'
| 'ExitActive'
| 'Down'
| 'KeyboardButton'
| 'ItemFocusAnim-darkGrey'
| 'ItemFocusAnim-grey'
| 'ItemFocusAnimBorder-darkGrey'
| 'focusAnimation'
| 'hoverAnimation',
string
>;
export const quickAccessMenuClasses: QuickAccessMenuClasses = findModule(
(mod) => typeof mod === 'object' && mod?.Title?.includes('quickaccessmenu'),
);
/**
* @depreciated please use quickAccessMenuClasses instead
*/
export const staticClasses = quickAccessMenuClasses;
export const scrollPanelClasses: ScrollPanelClasses = findModule(
(mod) => typeof mod === 'object' && mod?.ScrollPanel?.includes('scrollpanel'),
);
/**
* @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' && typeof mod?.PanelSection === 'string' && 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_'),
);
export const gamepadUIClasses: GamepadUIClasses = findModule(
(mod) => typeof mod === 'object' && mod?.BasicUiRoot?.includes('gamepadui_'),
);

View File

@@ -0,0 +1,6 @@
import {SteamClient} from "./steam-client";
export * from "./steam-client/shared";
declare global {
var SteamClient: SteamClient;
}

2
src/globals/index.ts Normal file
View File

@@ -0,0 +1,2 @@
export * from './SteamClient';
export * from './stores';

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,105 @@
import { EResult, JsPbMessage, OperationResponse } from "./shared";
import {EOSType} from "./system";
export interface Auth {
/**
* @returns a boolean indicating if the operation succeeded.
*/
ClearCachedSignInPin(): Promise<boolean>;
CurrentUserHasCachedSignInPin(): Promise<boolean>;
GetLocalHostname(): Promise<string>;
/**
* @returns a ProtoBuf message. If deserialized, returns {@link CAuthentication_DeviceDetails}.
*/
GetMachineID(): Promise<ArrayBuffer>;
GetRefreshInfo(): Promise<AuthRefreshInfo>;
GetSteamGuardData(param0: string): Promise<SteamGuardData>;
/**
* "Secured" refers to unshared.
*/
IsSecureComputer(): Promise<boolean>;
SetCachedSignInPin(pin: string): Promise<boolean>;
SetLoginToken(refreshToken: string, accountName: string): Promise<OperationResponse>;
SetSteamGuardData(accountName: string, newGuardData: string): void;
StartSignInFromCache(accountName: string, offlineMode: boolean): Promise<OperationResponse | void>;
UserHasCachedSignInPin(accountName: string): Promise<boolean>;
ValidateCachedSignInPin(accountName: string, pin: string): Promise<boolean>;
}
export interface AuthRefreshInfo {
reason: number;
account_name: string;
login_id_token: string;
}
export interface SteamGuardData {
data: string;
eresult: EResult;
}
/**
* `deserializeBinary` argument:
* ```
* [
* await SteamClient.System.GetOSType(),
* await SteamClient.Auth.GetLocalHostname(),
* await SteamClient.Auth.GetMachineID(),
* ];
* ```
*/
export interface CAuthentication_DeviceDetails extends JsPbMessage {
client_count(): number | undefined;
device_friendly_name(): string | undefined;
gaming_device_type(): EGamingDeviceType | undefined;
machine_id(): Uint8Array | string;
os_type(): EOSType | undefined;
platform_type(): EAuthTokenPlatformType | undefined;
set_client_count(): any;
set_device_friendly_name(): any;
set_gaming_device_type(): any;
set_machine_id(): any;
set_os_type(): any;
set_platform_type(): any;
}
export enum EAuthTokenPlatformType {
Unknown,
SteamClient,
WebBrowser,
MobileApp,
}
export enum EGamingDeviceType {
Unknown,
StandardPC,
Console = 256,
PS3 = 272,
Steambox = 288,
Tesla = 320,
Handheld = 512,
Phone = 528,
SteamDeck = 544,
}

View File

@@ -0,0 +1,56 @@
import { EResult, Unregisterable } from "./shared";
export interface Broadcast {
/**
* Approves a viewer request for the broadcast.
* @param steamId64 The SteamID64 of the user whose request is to be approved.
*/
ApproveViewerRequest(steamId64: string, param1: number): void;
/**
* Invites a user identified by their SteamID64 to watch the broadcast.
* @param steamId64 The SteamID64 of the user to invite.
*/
InviteToWatch(steamId64: string): Promise<EResult>;
/**
* Registers a callback to be called when the broadcast status changes.
* @param callback The callback function to be called.
* @returns an object that can be used to unregister the callback.
*/
RegisterForBroadcastStatus(callback: (status: BroadcastStatus) => void): Unregisterable;
/**
* Registers a callback to be called when viewer requests are received.
* @param callback The callback function to be called.
* @returns an object that can be used to unregister the callback.
*/
RegisterForViewerRequests(
callback: (viewerFriendCode: number, param1: number, param2: number) => void,
): Unregisterable;
/**
* Rejects a viewer request for the broadcast.
* @param steamId64 The SteamID64 of the user whose request is to be rejected.
*/
RejectViewerRequest(steamId64: string, param1: number): void;
/**
* Stops the broadcast.
*/
StopBroadcasting(): void;
}
export interface BroadcastStatus {
broadcastid: string;
nViewers: number;
nRequests: number;
bIsBroadcasting: boolean;
bIsRecordingDesktop: boolean;
eBroadcastReady: EResult;
bBroadcastCapable: boolean;
bMicrophoneEnabled: boolean;
bMicrophoneActive: boolean;
nCurrentFPS: number;
nUploadKbps: number;
}

View File

@@ -0,0 +1,91 @@
import { Unregisterable } from "./shared";
export interface Browser {
AddWordToDictionary(word: string): void;
ClearAllBrowsingData(): void;
ClearHistory(): void;
CloseDevTools(): void;
GetBrowserID(): Promise<number>;
GetSpellingSuggestions(word: string): string[];
GetSteamBrowserID(): Promise<number>; // 16-bit unsigned integer?
/**
* Hides the mouse cursor until input.
*/
HideCursorUntilMouseEvent(): void;
/**
* yup that's right, clientY and clientX are reversed
*/
InspectElement(clientY: number, clientX: number): void;
NotifyUserActivation(): void;
OpenDevTools(): void;
/**
* Pastes the clipboard contents.
*/
Paste(): void;
/**
* @note Not available on a created BrowserView.
* @todo unconfirmed
*/
RegisterForGestureEvents(callback: (gesture: TouchGesture) => void): Unregisterable;
/**
* @note Not available on a created BrowserView.
*/
RegisterForOpenNewTab: Unregisterable;
ReplaceMisspelling(param0: string): void;
/**
* Restarts the Steam JS context.
*/
RestartJSContext(): void;
SetBackgroundThrottlingDisabled(value: boolean): void;
SetPendingFilePath(path: string): Promise<boolean>;
SetShouldExitSteamOnBrowserClosed(value: boolean): Promise<void>;
SetTouchGesturesToCancel(gestures: ETouchGesture[]): void;
/**
* Prompts and downloads a file.
* @param url The URL of the file to download.
*/
StartDownload(url: string): void;
}
export interface TouchGesture {
eTouchGesture: ETouchGesture;
x: number;
y: number;
}
export enum ETouchGesture {
None,
Touch,
Tap,
DoubleTap,
ShortPress,
LongPress,
LongTap,
TwoFingerTap,
TapCancelled,
PinchBegin,
PinchUpdate,
PinchEnd,
FlingStart,
FlingCancelled,
}

View File

@@ -0,0 +1,39 @@
import { BrowserContext } from "./shared";
export interface ClientNotifications {
/**
* Displays a Steam notification.
* @param notification Notification type.
* @param options Stringified object of {@link SteamNotificationOptions}.
* @param callback
*/
DisplayClientNotification(
notification: EClientUINotificationType,
options: string,
callback: (context: BrowserContext) => void,
): void;
/**
* @param notificationId The ID of the notification to handle.
* @param handleAction `true` to execute the callback function associated with the notification.
*/
OnRespondToClientNotification(notificationId: number, handleAction: boolean): void;
}
export interface SteamNotificationOptions {
body: string;
chatroomgroupid?: number;
chatroomid?: number;
icon?: string;
state: string;
/** A Steam64 ID. */
steamid: string;
tag?: string;
title?: string;
}
export enum EClientUINotificationType {
GroupChatMessage = 1,
FriendChatMessage,
FriendPersonaState,
}

View File

@@ -0,0 +1,14 @@
export interface Cloud {
/**
* Resolves a synchronization conflict for an app in the cloud.
* @param appId The ID of the app with the sync conflict.
* @param keepLocal Whether to keep the local version during conflict resolution.
*/
ResolveAppSyncConflict(appId: number, keepLocal: boolean): void;
/**
* Retries syncing an app with the cloud.
* @param appId The ID of the app to retry syncing.
*/
RetryAppSync(appId: number): void;
}

View File

@@ -0,0 +1,24 @@
export interface CommunityItems {
/*
DownloadMovie(e) {
return (0, o.mG)(this, void 0, void 0, (function* () {
if (0 != e.movie_webm_local_path.length) return !0;
let t = yield SteamClient.CommunityItems.DownloadItemAsset(e.communityitemid, w, e.movie_webm),
n = 1 == t.result;
if (n) {
e.movie_webm_local_path = t.path;
let n = [];
this.m_startupMovies.forEach((t => {
t.movie_webm == e.movie_webm ? n.push(e) : n.push(t)
})), this.m_startupMovies = n
}
return n
}))
}
*/
DownloadItemAsset(communityItemId: string, param1: any, param2: string): any;
GetItemAssetPath(communityItemId: string, param1: any, param2: string): any;
RemoveDownloadedItemAsset(communityItemId: string, param1: any, param2: string): any;
}

View File

@@ -0,0 +1,43 @@
import { Unregisterable } from "./shared";
/**
* Represents the console functionality for executing commands and handling spew output.
*/
export interface Console {
/**
* Executes a console command.
* @param command The command to execute in the console.
*/
ExecCommand(command: string): void;
/**
* Retrieves autocomplete suggestions for a given console command.
* @param command The console command to provide autocomplete suggestions for.
* @returns an array of autocomplete suggestions.
*/
GetAutocompleteSuggestions(command: string): Promise<string[]>;
/**
* Registers a callback function to receive spew output.
* @param callback The callback function that will receive spew output.
* @returns an object that can be used to unregister the callback.
*/
RegisterForSpewOutput(callback: (output: SpewOutput) => void): Unregisterable;
}
export type SpewType_t = "assert" | "error" | "warning" | "info" | "input";
/**
* Represents spew output information.
*/
export interface SpewOutput {
/**
* The content of the spew output.
*/
spew: string;
/**
* The type or category of the spew output.
*/
spew_type: SpewType_t;
}

View File

@@ -0,0 +1,12 @@
export interface Customization {
GenerateLocalStartupMoviesThumbnails(param0: number): Promise<number>;
//param0: "startupmovies"
GetDownloadedStartupMovies(param0: string): Promise<StartupMovie[]>;
GetLocalStartupMovies(): Promise<StartupMovie[]>;
}
export interface StartupMovie {
strMovieURL: string;
}

View File

@@ -0,0 +1,175 @@
import { Unregisterable } from "./shared";
import {EAppUpdateError} from "./App";
/**
* Represents functions related to managing downloads in Steam.
*/
export interface Downloads {
/**
* Enables or disables all downloads in Steam.
* @param enable True to enable downloads, false to disable.
*/
EnableAllDownloads(enable: boolean): void;
/**
* Moves the update for a specific app down the download queue.
* @param appId The ID of the application to move.
*/
MoveAppUpdateDown(appId: number): void;
/**
* Moves the update for a specific app up the download queue.
* @param appId The ID of the application to move.
*/
MoveAppUpdateUp(appId: number): void;
PauseAppUpdate(appId: number): void; // Broken? It seems to be removing it from download list like RemoveFromDownloadList
/**
* Adds the update for a specific app to the download queue.
* @param appId The ID of the application to queue.
*/
QueueAppUpdate(appId: number): void;
/**
* Registers a callback function to be called when download items change.
* @param callback The callback function to be called.
* @returns an object that can be used to unregister the callback.
*/
RegisterForDownloadItems(
callback: (isDownloading: boolean, downloadItems: DownloadItem[]) => void,
): Unregisterable;
/**
* Registers a callback function to be called when download overview changes.
* @param callback The callback function to be called.
* @returns an object that can be used to unregister the callback.
*/
RegisterForDownloadOverview(callback: (overview: DownloadOverview) => void): Unregisterable;
/**
* Removes the update for a specific app from the download list and places it in the unscheduled list.
* @param appId The ID of the application to remove.
*/
RemoveFromDownloadList(appId: number): void;
/**
* Resumes the update for a specific app in the queue.
* @param appId The ID of the application to resume.
*/
ResumeAppUpdate(appId: number): void;
/**
* Sets an app to launch when its download is complete.
* @param appId The ID of the application to set.
*/
SetLaunchOnUpdateComplete(appId: number): void;
/**
* Sets the queue index for an app in the download queue.
* @param appId The ID of the application to set the index for.
* @param index The index to set.
* @remarks Index of 0 is the current download in progress.
*/
SetQueueIndex(appId: number, index: number): void;
/**
* Suspends or resumes download throttling.
* @param suspend Whether to suspend or resume download throttling.
*/
SuspendDownloadThrottling(suspend: boolean): void;
/**
* Suspends or resumes local transfers.
* @param suspend Whether to suspend or resume local transfers.
*/
SuspendLanPeerContent(suspend: boolean): void;
}
export interface DownloadItem {
/** True if this app is currently downloading */
active: boolean;
/** Appid of app */
appid: number;
/** Current build ID for the installed app, zero if the app isn't installed yet */
buildid: number;
/** True if this update has been completed */
completed: boolean;
/** For completed downloads, time of completion, 0 if not completed */
completed_time: number;
deferred_time: number;
/** Bytes already downloaded, sum across all content types */
downloaded_bytes: number;
/** If true, game will launch when its download completes successfully */
launch_on_completion: boolean;
/** True if this app has been paused by the user or the system */
paused: boolean;
/** Queue index, -1 if the item is unqueued */
queue_index: number;
/** Build ID that this download is moving towards. This can be the same as buildid.*/
target_buildid: number;
/** Total bytes to download, sum across all content types */
total_bytes: number;
/**
* Update error description, when paused and there has been an error.
* Unlocalized and shouldn't be displayed to the user.
*/
update_error: string;
update_result: EAppUpdateError;
update_type_info: UpdateTypeInfo[];
}
export interface DownloadOverview {
/** Set if we are downloading from LAN peer content server */
lan_peer_hostname: string;
/** True if all downloads are paused */
paused: boolean;
/** True if download throttling has been temporarily suspended for the current download */
throttling_suspended: boolean;
/** Appid of currently updating app */
update_appid: number;
/** Bytes already downloaded */
update_bytes_downloaded: number;
/** Bytes already processed in current phase - resets to zero when update stage changes */
update_bytes_processed: number;
/** Bytes already staged */
update_bytes_staged: number;
/** Total bytes to download */
update_bytes_to_download: number;
/** Total bytes to process in current phase - resets to zero when update stage changes */
update_bytes_to_process: number;
/** Total bytes to be staged */
update_bytes_to_stage: number;
/** Current disk throughput estimate */
update_disc_bytes_per_second: number;
/** True if the current update is an initial install */
update_is_install: boolean;
/** True if download and staging sizes are prefetch estimates */
update_is_prefetch_estimate: boolean;
/** True if the current update is for shader update */
update_is_shader: boolean;
/** True if the client is running in peer content server mode serving other peers */
update_is_upload: boolean;
/** True if the current update is for workshop content */
update_is_workshop: boolean;
/** Current bandwidth estimate for download */
update_network_bytes_per_second: number;
/** Peak bandwidth estimate for download */
update_peak_network_bytes_per_second: number;
/** Estimate of remaining time (in seconds) until download completes (not including staging) */
update_seconds_remaining: number;
/** Time current update started */
update_start_time: number;
update_state: 'None' | 'Starting' | 'Updating' | 'Stopping';
}
export interface UpdateTypeInfo {
/** True if this content type had an update and it has completed */
completed_update: boolean;
/** Bytes already downloaded for this content type */
downloaded_bytes: number;
/** True if this content type has or had an update */
has_update: boolean;
/** Total bytes to download for this content type */
total_bytes: number;
}

View File

@@ -0,0 +1,30 @@
import { EResult, Unregisterable } from "./shared";
/**
* Represents functions related to Steam Family Sharing.
*/
export interface FamilySharing {
GetAvailableLenders(appId: number): Promise<Lender[]>;
RegisterForKickedBorrower: Unregisterable;
SetPreferredLender(appId: number, param1: number): Promise<EResult>;
}
interface LenderDLC {
rtStoreAssetModifyTime: number;
strHeaderFilename: string;
strName: string;
unAppID: number;
}
export interface Lender {
/**
* A Steam64 ID.
*/
steamid: string;
appid: number;
numDlc: number;
bPreferred: boolean;
vecDLC: LenderDLC[];
}

View File

@@ -0,0 +1,80 @@
import { VDFBoolean_t } from "./shared";
/**
* Represents friend settings and configuration.
*/
export interface FriendSettings {
/**
* Retrieves a list of enabled friend settings features.
* @returns an array of enabled friend settings features.
*/
GetEnabledFeatures(): Promise<FriendSettingsFeatureObject[]>;
/**
* Registers a callback function to be notified of friend settings changes.
* @param callback The callback function to be called when friend settings change.
* @remarks The callback receives a JSON object string which may be parsed into {@link FriendSettingsChange}.
*/
RegisterForSettingsChanges(callback: (settings: string) => void): void;
/**
* @param details Stringified {@link FriendSettingsChange}.
*/
SetFriendSettings(details: string): void;
}
export enum EChatFlashMode {
Always,
Minimized,
Never,
}
export interface FriendSettingsFeatureObject {
feature: FriendSettingsFeature_t;
bEnabled: boolean;
}
export type FriendSettingsFeature_t =
| "DoNotDisturb"
| "FriendsFilter"
| "LoaderWindowSynchronization"
| "NewVoiceHotKeyState"
| "NonFriendMessageHandling"
| "PersonaNotifications"
| "ServerVirtualizedMemberLists"
| "SteamworksChatAPI";
export type FriendSettingsEnabledFeatures<T> = {
[feature in FriendSettingsFeature_t]: T;
};
export interface FriendSettingsChange {
bNotifications_ShowIngame: VDFBoolean_t;
bNotifications_ShowOnline: VDFBoolean_t;
bNotifications_ShowMessage: VDFBoolean_t;
bNotifications_EventsAndAnnouncements: VDFBoolean_t;
bSounds_PlayIngame: VDFBoolean_t;
bSounds_PlayOnline: VDFBoolean_t;
bSounds_PlayMessage: VDFBoolean_t;
bSounds_EventsAndAnnouncements: VDFBoolean_t;
bAlwaysNewChatWindow: VDFBoolean_t;
bForceAlphabeticFriendSorting: VDFBoolean_t;
nChatFlashMode: EChatFlashMode;
bRememberOpenChats: VDFBoolean_t;
bCompactQuickAccess: VDFBoolean_t;
bCompactFriendsList: VDFBoolean_t;
bNotifications_ShowChatRoomNotification: VDFBoolean_t;
bSounds_PlayChatRoomNotification: VDFBoolean_t;
bHideOfflineFriendsInTagGroups: VDFBoolean_t;
bHideCategorizedFriends: VDFBoolean_t;
bCategorizeInGameFriendsByGame: VDFBoolean_t;
nChatFontSize: number;
b24HourClock: VDFBoolean_t;
bDoNotDisturbMode: VDFBoolean_t;
bDisableEmbedInlining: VDFBoolean_t;
bSignIntoFriends: VDFBoolean_t;
bDisableSpellcheck: VDFBoolean_t;
bDisableRoomEffects: VDFBoolean_t;
bAnimatedAvatars: VDFBoolean_t;
featuresEnabled: FriendSettingsEnabledFeatures<VDFBoolean_t>;
}

View File

@@ -0,0 +1,70 @@
import { Unregisterable } from "./shared";
/**
* Represents functions related to managing friends in Steam.
*/
export interface Friends {
/**
* Adds a user to the friend list.
* @param steamId The Steam ID of the user to add as a friend.
* @returns `true` if the friend was added successfully.
*/
AddFriend(steamId: string): Promise<boolean>;
/**
* @returns a list of players you recently played with.
*/
GetCoplayData(): Promise<CoplayData>;
InviteUserToCurrentGame(steam64Id: string, steamIdTarget: string): Promise<boolean>;
/**
* Invites a user to a specific game.
* @param steamId The Steam ID of the user to invite.
* @param appId The ID of the game to invite the user to.
* @param connectString Additional parameters for the invitation.
* @returns `true` if the user was invited successfully.
*/
InviteUserToGame(steamId: string, appId: number, connectString: string): Promise<boolean>;
/**
* Invites a user to a specific lobby.
* @returns `true` if the user was invited successfully.
*/
InviteUserToLobby(steam64Id: string, steamIdTarget: string): Promise<boolean>;
InviteUserToRemotePlayTogetherCurrentGame(steam64Id: string): Promise<boolean>;
RegisterForMultiplayerSessionShareURLChanged(
appId: number,
callback: (param0: string, param1: string) => void,
): Unregisterable;
RegisterForVoiceChatStatus(callback: (status: VoiceChatStatus) => void): Unregisterable;
/**
* Removes a user from the friend list.
* @param steamId The Steam ID of the user to remove from the friend list.
* @returns `true` if the friend was removed successfully.
*/
RemoveFriend(steamId: string): Promise<boolean>;
ShowRemotePlayTogetherUI(): void;
}
export interface CoplayData {
currentUsers: CoplayUser[];
recentUsers: CoplayUser[];
}
export interface CoplayUser {
accountid: number;
rtTimePlayed: number;
appid: number;
}
export interface VoiceChatStatus {
bVoiceChatActive: boolean;
bMicMuted: boolean;
bOutputMuted: boolean;
}

View File

@@ -0,0 +1,73 @@
import { EResult, OperationResponse } from "./shared";
export interface GameNotes {
/**
* @returns a boolean indicating whether the operation was successful.
*/
DeleteImage(param0: string): Promise<boolean>;
/**
* @returns a boolean indicating whether the operation was successful.
*/
DeleteNotes(param0: string): Promise<boolean>;
GetNotes(filenameForNotes: string, directoryForNoteImages: string): Promise<Notes>;
GetNotesMetadata(note: string): Promise<NoteMetadata>;
GetNumNotes(): Promise<number>;
GetQuota: Promise<NotesQuota>;
IterateNotes(appId: number, length: number): Promise<NoteMetadata[]>;
ResolveSyncConflicts(param0: boolean): Promise<EResult>;
/**
* @param notes Escaped JSON array of {@link Note}.
*/
SaveNotes(filenameForNotes: string, notes: string): Promise<EResult>;
SyncToClient(): Promise<EResult>;
SyncToServer(): Promise<EResult>;
/**
* @param mimeType Image MIME type.
* @param base64 Image contents in base64.
* @returns an image file name with its extension that's meant to be used as a part of some URL. (todo)
* @throws OperationResponse if invalid MIME type or unable to parse base64 BUT NOT if it failed.
*/
UploadImage(imageFileNamePrefix: string, mimeType: string, base64: string): Promise<EResult | OperationResponse>;
}
export interface Note {
appid: number;
id: string;
/**
* Note contents in BB code.
*/
content: string;
ordinal: number;
time_created: number;
time_modified: number;
title: string;
}
interface Notes {
result: EResult;
/**
* Escaped JSON array of {@link Note}. Not present if {@link result} is {@link EResult.FileNotFound}.
*/
notes?: string;
}
interface NoteMetadata {
filename: string;
filesize: number;
result: EResult;
timestamp: number;
}
interface NotesQuota {
bytes: number;
bytesAvailable: number;
numFiles: number;
numFilesAvailable: number;
}

View File

@@ -0,0 +1,29 @@
import type { JsPbMessage, Unregisterable } from "./shared";
export interface GameRecording {
/**
* If `data` is deserialized, returns {@link CGameRecording_AudioSessionsChanged_Notification}.
*/
RegisterForAudioSessionsChanged(callback: (data: ArrayBuffer) => void): Unregisterable;
SetAudioSessionCaptureState(id: string, name: string, state: boolean): void;
}
export interface AudioSession {
id(): string | undefined;
name(): string | undefined;
is_system(): boolean | undefined;
is_muted(): boolean | undefined;
is_active(): boolean | undefined;
is_captured(): boolean | undefined;
recent_peak(): number | undefined;
is_game(): boolean | undefined;
is_steam(): boolean | undefined;
is_saved(): boolean | undefined;
}
/**
* @note Taken from https://github.com/SteamDatabase/SteamTracking/blob/master/Protobufs/steammessages_gamerecording_objects.proto
*/
export interface CGameRecording_AudioSessionsChanged_Notification extends JsPbMessage {
sessions(): AudioSession[];
}

View File

@@ -0,0 +1,59 @@
import { Unregisterable } from "./shared";
import {AppAchievements} from "./App";
import { Screenshot } from "./Screenshots";
/**
* Represents functions related to Steam Game Sessions.
*/
export interface GameSessions {
/**
* Registers a callback function to be called when an achievement notification is received.
* @param callback The callback function to be called.
* @returns an object that can be used to unregister the callback.
*/
RegisterForAchievementNotification(
callback: (notification: AchievementNotification) => void,
): Unregisterable;
/**
* Registers a callback function to be called when an app lifetime notification is received.
* @param callback The callback function to be called.
* @returns an object that can be used to unregister the callback.
*/
RegisterForAppLifetimeNotifications(
callback: (notification: AppLifetimeNotification) => void,
): Unregisterable;
/**
* Registers a callback function to be called when a screenshot notification is received.
* @param callback The callback function to be called.
* @returns an object that can be used to unregister the callback.
*/
RegisterForScreenshotNotification(
callback: (notification: ScreenshotNotification) => void,
): Unregisterable;
}
/**
* @prop unAppID is not properly set by Steam for non-steam game shortcuts, so it defaults to 0 for them
*/
interface GameSessionNotificationBase {
unAppID: number;
}
export interface AchievementNotification extends GameSessionNotificationBase {
achievement: AppAchievements;
nCurrentProgress: number;
nMaxProgress: number;
}
export interface AppLifetimeNotification extends GameSessionNotificationBase {
nInstanceID: number;
bRunning: boolean;
}
export interface ScreenshotNotification extends GameSessionNotificationBase {
details: Screenshot;
hScreenshot: number;
strOperation: "deleted" | "written";
}

View File

@@ -0,0 +1,810 @@
import { Unregisterable } from "./shared";
/**
* Represents functions related to input and controllers in Steam.
*/
export interface Input {
CalibrateControllerIMU(controllerIndex: any): any;
CalibrateControllerJoystick(controllerIndex: any): any;
CalibrateControllerTrackpads(controllerIndex: any): any;
CancelGyroSWCalibration(): any;
ClearSelectedConfigForApp(appId: number, controllerIndex: number): any;
CloseDesktopConfigurator: any;
/**
* Writes text.
* @param textToWrite The text to write.
*/
ControllerKeyboardSendText(textToWrite: string): void;
/**
* Sets a specified key's pressed state.
* @param key The key index to set the state for.
* @param state `true` for pressed, `false` otherwise.
* @example
* Send paste command:
* ```
* SteamClient.Input.ControllerKeyboardSetKeyState(EHIDKeyboardKey.LControl, true);
* SteamClient.Input.ControllerKeyboardSetKeyState(EHIDKeyboardKey.V, true);
* SteamClient.Input.ControllerKeyboardSetKeyState(EHIDKeyboardKey.V, false);
* SteamClient.Input.ControllerKeyboardSetKeyState(EHIDKeyboardKey.LControl, false);
* ```
*/
ControllerKeyboardSetKeyState(key: EHIDKeyboardKey, state: boolean): void;
DecrementCloudedControllerConfigsCounter(): any;
DeletePersonalControllerConfiguration(param0: any): any;
//f.Debug("sending to client"), this.SetEditingConfigurationValue(e, t, c.QU, (e => SteamClient.Input.DuplicateControllerConfigurationSourceMode(this.m_unControllerIndex, e))), this.SaveEditingConfiguration(e), this
DuplicateControllerConfigurationSourceMode(controllerIndex: number, param1: any): any;
EndControllerDeviceSupportFlow(): any;
ExportCurrentControllerConfiguration(controllerIndex: number, appId: number, param2: number, title: string, description: string, param5: string): Promise<any>;
ForceConfiguratorFocus(param0: boolean): any;
ForceSimpleHapticEvent(param0: number, param1: number, param2: number, param3: number, param4: number): any;
FreeControllerConfig(m_ChordSummaryConfiguration: any): any;
GetConfigForAppAndController(appId: number, unControllerIndex: number): any;
/**
* Retrieves the controller mapping string for the specified controller index.
* @param unControllerIndex The controller index.
* @returns the controller mapping string.
*/
GetControllerMappingString(unControllerIndex: number): Promise<string>;
GetControllerPreviouslySeen(): Promise<number[]>;
GetSteamControllerDongleState(): Promise<boolean>;
GetTouchMenuIconsForApp(appId: number): Promise<any>;
GetXboxDriverInstallState(): Promise<any>; // "{"nResult":0}"
IdentifyController(controllerIndex: number): any;
InitControllerSounds(): any;
InitializeControllerPersonalizationSettings(controllerIndex: number): any;
ModalKeyboardDismissed(): void;
OpenDesktopConfigurator: any;
PreviewConfigForAppAndController(appId: number, controllerIndex: number, workshopUri: string): any;
PreviewControllerLEDColor(flHue: number, flSaturation: number, flBrightness: number): any;
QueryControllerConfigsForApp(appId: number, controllerIndex: number, param2: boolean): any;
RegisterForActiveControllerChanges: Unregisterable; // {"nActiveController":0}
//param0 - e possibly appid?
//param1 - some index?
RegisterForConfigSelectionChanges(callback: (param0: number, param1: number) => void): Unregisterable;
RegisterForControllerAccountChanges: Unregisterable;
RegisterForControllerAnalogInputMessages(
callback: (msgs: ControllerAnalogInputMessage[]) => void,
): Unregisterable;
RegisterForControllerBatteryChanges(callback: any): Unregisterable;
RegisterForControllerCommandMessages(
callback: (msg: ControllerCommandMessage) => void,
): Unregisterable;
/**
* Registers a callback for changes in controller configuration cloud state.
* @param callback The callback function for config cloud state changes.
* @returns an object that can be used to unregister the callback.
*/
RegisterForControllerConfigCloudStateChanges(
callback: (state: ControllerConfigCloudState) => void,
): Unregisterable;
/**
* Registers a callback for receiving controller configuration info messages (controller layouts query, personal controller layout query).
* @param callback The callback function for controller config info messages.
* @returns an object that can be used to unregister the callback.
* @remarks Do Not Use, this will break the controller layout selection unless you know what you are doing.
*/
RegisterForControllerConfigInfoMessages(
callback: (
msgs: ControllerConfigInfoMessageList[] | ControllerConfigInfoMessageQuery[],
) => void,
): Unregisterable;
/**
* Registers a callback function to be invoked when controller input messages are received.
* @param callback The callback function to be invoked when controller input messages are received.
* @returns an object that can be used to unregister the callback.
*/
RegisterForControllerInputMessages(
callback: (controllerIndex: number, gamepadButton: ControllerInputGamepadButton, isButtonPressed: boolean) => void,
): Unregisterable;
RegisterForControllerListChanges(callback: (controllerListChanges: ControllerInfo[]) => void): Unregisterable;
/**
* Registers a callback for changes in the controller state (buttons presses, triggers presses, joystick changes etc...).
* @param callback The callback function for controller state changes.
* @returns an object that can be used to unregister the callback.
*/
RegisterForControllerStateChanges(
callback: (changes: ControllerStateChange[]) => void,
): Unregisterable;
RegisterForDualSenseUpdateNotification(callback: (m_strDualSenseUpdateProduct: string) => void): Unregisterable;
/**
* Registers a callback for receiving game keyboard messages (text field popup for inputting text for games when in character creation or etc...).
* @param callback The callback function for game keyboard messages.
* @returns an object that can be used to unregister the callback.
*/
RegisterForGameKeyboardMessages(callback: (msg: GameKeyboardMessage) => void): Unregisterable;
RegisterForRemotePlayConfigChanges(callback: () => void): Unregisterable;
//data.appId, data.ulConfigId
RegisterForShowControllerLayoutPreviewMessages(callback: (data: any) => void): Unregisterable;
/*
onTouchMenuInput(e) {
for (let t = 0; t < e.length; t++) {
const n = this.TouchMenuGetKey(e[t]), o = this.m_mapActiveTouchMenus.get(n);
void 0 !== o && o.updateTouchMenuState(e[t])
}
}
*/
RegisterForTouchMenuInputMessages(callback: (inputs: number[]) => void): Unregisterable;
RegisterForTouchMenuMessages(callback: (msg: TouchMenuMessage) => void): Unregisterable;
//param0 - index?
RegisterForUIVisualization(param0: any, param1: any, param2: any): Unregisterable;
RegisterForUnboundControllerListChanges(callback: (m_unboundControllerList: any) => void): Unregisterable; // param0 is an array
/*
OnDismissKeyboardMessage(e) {
this.m_WindowStore.SteamUIWindows.forEach((e => e.VirtualKeyboardManager.SetVirtualKeyboardHidden(e.BrowserWindow)))
}
*/
RegisterForUserDismissKeyboardMessages(callback: (param0: any) => void): Unregisterable;
RegisterForUserKeyboardMessages: Unregisterable;
RequestGyroActive(controllerIndex: number, param1: boolean): any;
RequestRemotePlayControllerConfigs(param0: any): any;
ResetControllerBindings(param0: any): any;
ResolveCloudedControllerConfigConflict(param0: any): any;
RestoreControllerPersonalizationSettings(controllerIndex: number): any;
SaveControllerCalibration(controllerIndex: number): any;
SaveControllerPersonalizationSettings(param0: any): any;
SaveControllerSounds: any;
SaveEditingControllerConfiguration(controllerIndex: number, sharedConfig: boolean): any;
//this.SetEditingConfigurationValue(e, t, c.sL, (e => SteamClient.Input.SetControllerConfigurationModeShiftBinding(this.m_unControllerIndex, e)))
SetControllerConfigurationModeShiftBinding(controllerIndex: number, param1: any): any;
SetControllerHapticSetting(controllerIndex: number, eHapticSetting: any): any;
SetControllerMappingString(mapping: string): void;
SetControllerName(controllerIndex: number, controllerName: string): any;
SetControllerNintendoLayoutSetting: any;
SetControllerPersonalizationName: any;
//param0 - nLStickDeadzone, bSWAntiDrift, nRHapticStrength, flRPadPressureCurve
/*
SteamClient.Input.SetControllerPersonalizationSetting("nLStickDeadzone", e.nLStickDeadzone),
SteamClient.Input.SetControllerPersonalizationSetting("nRStickDeadzone", e.nRStickDeadzone),
SteamClient.Input.SetControllerPersonalizationSetting("bSWAntiDrift", e.bSWAntiDrift ? 1 : 0),
SteamClient.Input.SetControllerPersonalizationSetting("nLHapticStrength", e.nLHapticStrength),
SteamClient.Input.SetControllerPersonalizationSetting("nRHapticStrength", e.nRHapticStrength),
SteamClient.Input.SetControllerPersonalizationSetting("flLPadPressureCurve", 100 * e.flLPadPressureCurve),
SteamClient.Input.SetControllerPersonalizationSetting("flRPadPressureCurve", 100 * e.flRPadPressureCurve),
SteamClient.Input.SetControllerPersonalizationSetting("ePlayerSlotLEDSetting", e),
SteamClient.Input.SetControllerPersonalizationSetting("GyroPreferenceData.nGyroSampleAngleOffsetX", e.nGyroSampleAngleOffsetX),
SteamClient.Input.SetControllerPersonalizationSetting("GyroPreferenceData.bMomentumEnabled", e.bMomentumEnabled ? 1 : 0),
SteamClient.Input.SetControllerPersonalizationSetting("GyroPreferenceData.nMomentumFrictionX", e.nMomentumFrictionX),
SteamClient.Input.SetControllerPersonalizationSetting("GyroPreferenceData.nMomentumFrictionY", e.nMomentumFrictionY),
SteamClient.Input.SetControllerPersonalizationSetting("GyroPreferenceData.nAccerationLevel", e.nAccerationLevel),
SteamClient.Input.SetControllerPersonalizationSetting("GyroPreferenceData.bInvertX", e.bInvertX ? 1 : 0),
SteamClient.Input.SetControllerPersonalizationSetting("GyroPreferenceData.bInvertY", e.bInvertY ? 1 : 0),
SteamClient.Input.SetControllerPersonalizationSetting("GyroPreferenceData.nRotationAngle", e.nRotationAngle),
SteamClient.Input.SetControllerPersonalizationSetting("GyroPreferenceData.nTriggerClamping", e.nTriggerClamping),
SteamClient.Input.SetControllerPersonalizationSetting("GyroPreferenceData.nTriggerClampingAmount", e.nTriggerClampingAmount),
SteamClient.Input.SetControllerPersonalizationSetting("GyroPreferenceData.nGyroEnableButton", e.nGyroEnableButton),
SteamClient.Input.SetControllerPersonalizationSetting("GyroPreferenceData.nGyroEnableButtonBehavior", e.nGyroEnableButtonBehavior),
*/
SetControllerPersonalizationSetting(param0: string, param1: number): any;
//param0 - flGyroStationaryTolerance, flAccelerometerStationaryTolerance,
/*
SteamClient.Input.SetControllerPersonalizationSettingFloat("GyroPreferenceData.flGyroNaturalSensitivity", e.flGyroNaturalSensitivity),
SteamClient.Input.SetControllerPersonalizationSettingFloat("GyroPreferenceData.flGyroXYRatio", e.flGyroXYRatio),
SteamClient.Input.SetControllerPersonalizationSettingFloat("GyroPreferenceData.flGyroSpeedDeadzone", e.flGyroSpeedDeadzone),
SteamClient.Input.SetControllerPersonalizationSettingFloat("GyroPreferenceData.flGyroPrecisionSpeed", e.flGyroPrecisionSpeed),
SteamClient.Input.SetControllerPersonalizationSettingFloat("flGyroStationaryTolerance", e.flGyroStationaryTolerance),
SteamClient.Input.SetControllerPersonalizationSettingFloat("flAccelerometerStationaryTolerance", e.flAccelerometerStationaryTolerance),
*/
SetControllerPersonalizationSettingFloat(param0: string, param1: number): any;
SetControllerRumbleSetting(controllerIndex: number, rumblePreference: EControllerRumbleSetting): any;
SetControllerUseUniversalFaceButtonGlyphs(controllerIndex: number, value: boolean): void;
SetCursorActionset(param0: boolean): any;
SetDualSenseUpdateNotification(param0: boolean): any
/*
SetEditingConfigurationValue(e, t, n, o) {
const a = new r.BinaryWriter;
n.serializeBinaryToWriter(n.fromObject(t), a);
const s = a.getResultBase64String();
f.Debug("SetEditingConfigurationValue serializeBinaryToWriter", (0, i.ZN)(t), s), this.EditingConfigurationWillUpdate(), this.m_updatingEditingConfigurationPromise = o(s).then((t => {
if (null == t) return f.Debug("SetEditingConfigurationValue returned nothing."), void (0, i.z)((() => this.UpdateEditingConfiguration(e, this.m_unControllerIndex, this.EditingConfiguration)));
const n = c.bE.deserializeBinary(t).toObject();
f.Debug("SetEditingConfigurationValue returned controller configuration.", n), this.UpdateEditingConfiguration(e, this.m_unControllerIndex, n), this.m_nEditNumber++, -1 == n.url.indexOf("autosave://") && this.SaveEditingConfiguration(e)
})).catch((e => {
f.Error("SetEditingConfigurationValue fail:", o, l.jt(e.result), e.message), this.m_bIsUpdatingActiveConfiguration = !1
}))
}
SetControllerActionSet(e, t) {
this.SetEditingConfigurationValue(e, t, c.X3, (e => SteamClient.Input.SetEditingControllerConfigurationActionSet(this.m_unControllerIndex, e)))
}
*/
SetEditingControllerConfigurationActionSet(controllerIndex: number, param1: any): any;
//this.SetEditingConfigurationValue(e, t, c.io, (e => SteamClient.Input.SetEditingControllerConfigurationInputActivator(this.m_unControllerIndex, e)))
SetEditingControllerConfigurationInputActivator(controllerIndex: number, param1: any): any;
//this.SetEditingConfigurationValue(e, t, c.tH, (e => SteamClient.Input.SetEditingControllerConfigurationInputActivatorEnabled(this.m_unControllerIndex, e)))
SetEditingControllerConfigurationInputActivatorEnabled(controllerIndex: number, param1: any): any;
//this.SetEditingConfigurationValue(e, t, c.J2, (e => SteamClient.Input.SetEditingControllerConfigurationInputBinding(this.m_unControllerIndex, e)))
SetEditingControllerConfigurationInputBinding(controllerIndex: number, param1: any): any;
//this.SetEditingConfigurationValue(e, t, c.Sz, (e => SteamClient.Input.SetEditingControllerConfigurationMiscSetting(this.m_unControllerIndex, e)))
SetEditingControllerConfigurationMiscSetting(controllerIndex: number, param1: any): any;
//f.Debug("sending to client"), this.SetEditingConfigurationValue(e, t, c.QU, (e => SteamClient.Input.SetEditingControllerConfigurationSourceMode(this.m_unControllerIndex, e)))
SetEditingControllerConfigurationSourceMode(controllerIndex: number, param1: any): any;
SetGamepadKeyboardText(param0: boolean, param1: string): any;
SetKeyboardActionset(param0: boolean, param1: boolean): any;
/**
* Sets the mouse position.
* @param pid 0
* @param x Mouse X position.
* @param y Mouse Y position.
*/
SetMousePosition(pid: number, x: number, y: number): void;
SetSelectedConfigForApp(appId: number, controllerIndex: number, url: string, param3: boolean): any;
SetSteamControllerDonglePairingMode(bEnable: boolean, bSilent: boolean): any;
SetVirtualMenuKeySelected(unControllerIndex: number, unMenuIndex: number, m_controllerMenuActiveMenuItem: number): any; //
SetWebBrowserActionset(param0: boolean): any;
SetXboxDriverInstallState(param0: any): any; // state
/**
* Opens the Steam Input controller settings.
* This function displays the Steam Input controller settings for configuration.
*/
ShowControllerSettings(): void;
StandaloneKeyboardDismissed(): any;
StartControllerDeviceSupportFlow(param0: any, param1: any, callback: (param2: any) => void): any;
/*
this.m_updatingEditingConfigurationPromise = SteamClient.Input.StartEditingControllerConfigurationForAppIDAndControllerIndex(e, t).then((n=>{
const o = c.bE.deserializeBinary(n).toObject();
f.Debug("Loaded controller config for appid", e, n, o),
(0,
i.z)((()=>this.UpdateEditingConfiguration(e, t, o)))
}
)).catch((n=>{
f.Debug("Loading controller config for appid rejected", e, n),
(0,
i.z)((()=>this.UpdateEditingConfiguration(e, t, null)))
}
))
*/
StartEditingControllerConfigurationForAppIDAndControllerIndex(m_appId: number, m_unControllerIndex: number): Promise<any>;
StartGyroSWCalibration(callback: () => void): any;
StopEditingControllerConfiguration(controllerIndex: number): any;
SwapControllerConfigurationSourceModes: any;
//this.SetEditingConfigurationValue(e, t, c.Qb, (e => SteamClient.Input.SwapControllerModeInputBindings(this.m_unControllerIndex, e)))
SwapControllerModeInputBindings(controllerIndex: number, param1: any): any;
SwapControllerOrder(controllerIndex1: number, controllerIndex2: number): any;
SyncCloudedControllerConfigs(): any;
// type - enum
/*
Off - 0, Tick, Click
*/
TriggerHapticPulse(controllerIndex: number, eHapticType: number, param2: number): any;
TriggerSimpleHapticEvent(
controllerIndex: number,
eHapticType: number,
unIntensity: number,
ndBGain: number,
param4: number,
): any;
UnregisterForControllerStateChanges(): void;
UnregisterForUIVisualization(controllerIndex: number): any;
UploadChangesForCloudedControllerConfigs(): any;
}
export enum EHIDKeyboardKey {
Invalid,
BeforeFirst = 3,
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z,
Key_1,
Key_2,
Key_3,
Key_4,
Key_5,
Key_6,
Key_7,
Key_8,
Key_9,
Key_0,
Return,
Escape,
Backspace,
Tab,
Space,
Dash,
Equals,
LeftBracket,
RightBracket,
Backslash,
Unused1,
Semicolon,
SingleQuote,
Backtick,
Comma,
Period,
ForwardSlash,
CapsLock,
F1,
F2,
F3,
F4,
F5,
F6,
F7,
F8,
F9,
F10,
F11,
F12,
PrintScreen,
ScrollLock,
Break,
Insert,
Home,
PageUp,
Delete,
End,
PageDown,
RightArrow,
LeftArrow,
DownArrow,
UpArrow,
NumLock,
KeypadForwardSlash,
KeypadAsterisk,
KeypadDash,
KeypadPlus,
KeypadEnter,
Keypad_1,
Keypad_2,
Keypad_3,
Keypad_4,
Keypad_5,
Keypad_6,
Keypad_7,
Keypad_8,
Keypad_9,
Keypad_0,
KeypadPeriod,
LAlt,
LShift,
LWin,
LControl,
RAlt,
RShift,
RWin,
RControl,
VolUp,
VolDown,
Mute,
Play,
Stop,
Next,
Prev,
AfterLast,
}
export interface ControllerAnalogInputMessage {
nA: number;
x: number;
y: number;
nC: number;
}
export interface ControllerCommandMessage {
/**
* @todo enum
*/
eAction: number;
nControllerIndex: number;
}
export interface ControllerConfigCloudState {
bSyncDone: boolean;
bSyncConflict: boolean;
bSyncError: boolean;
}
export interface ControllerConfigInfoMessage {
appID: number;
}
export interface ControllerConfigInfoMessageQuery extends ControllerConfigInfoMessage {
bPersonalQueryDone: boolean;
}
export interface ControllerConfigInfoMessageList extends ControllerConfigInfoMessage {
nControllerType: number;
publishedFileID: string;
accountID: number;
Title: string;
Description: string;
URL: string;
timeUpdated: string;
bOfficial: boolean;
bProgenitorOfficial: boolean;
bRecommended: boolean;
bProgenitorRecommended: boolean;
bUsesSIAPI: boolean;
bUsesMouse: boolean;
bUsesKeyboard: boolean;
bUsesGamepad: boolean;
/**
* @todo unconfirmed
*/
eExportType: EControllerConfigExportType;
playtime: string;
bSelected: boolean;
}
export enum EControllerConfigExportType {
Unknown,
PersonalLocal,
PersonalCloud,
Community,
Template,
Official,
OfficialDefault,
}
export enum EControllerRumbleSetting {
ControllerPreference,
Off,
On,
}
export enum ControllerInputGamepadButton {
GAMEPAD_BUTTON_A = 0,
GAMEPAD_BUTTON_B = 1,
GAMEPAD_BUTTON_X = 2,
GAMEPAD_BUTTON_Y = 3,
GAMEPAD_BUTTON_DPAD_UP = 4,
GAMEPAD_BUTTON_DPAD_RIGHT = 5,
GAMEPAD_BUTTON_DPAD_DOWN = 6,
GAMEPAD_BUTTON_DPAD_LEFT = 7,
GAMEPAD_BUTTON_MENU = 8,
GAMEPAD_BUTTON_VIEW = 9,
GAMEPAD_LEFTPAD_UP = 10,
GAMEPAD_LEFTPAD_DOWN = 11,
GAMEPAD_LEFTPAD_LEFT = 12,
GAMEPAD_LEFTPAD_RIGHT = 13,
GAMEPAD_LEFTPAD_ANALOG = 14,
GAMEPAD_RIGHTPAD_UP = 15,
GAMEPAD_RIGHTPAD_DOWN = 16,
GAMEPAD_RIGHTPAD_LEFT = 17,
GAMEPAD_RIGHTPAD_RIGHT = 18,
GAMEPAD_RIGHTPAD_ANALOG = 19,
GAMEPAD_LEFTSTICK_UP = 20,
GAMEPAD_LEFTSTICK_DOWN = 21,
GAMEPAD_LEFTSTICK_LEFT = 22,
GAMEPAD_LEFTSTICK_RIGHT = 23,
GAMEPAD_LEFTSTICK_ANALOG = 24,
GAMEPAD_LEFTSTICK_CLICK = 25,
GAMEPAD_LTRIGGER_ANALOG = 26,
GAMEPAD_RTRIGGER_ANALOG = 27,
GAMEPAD_BUTTON_LTRIGGER = 28,
GAMEPAD_BUTTON_RTRIGGER = 29,
GAMEPAD_BUTTON_LSHOULDER = 30,
GAMEPAD_BUTTON_RSHOULDER = 31,
GAMEPAD_BUTTON_LBACK = 32,
GAMEPAD_BUTTON_RBACK = 33,
GAMEPAD_BUTTON_GUIDE = 34,
GAMEPAD_BUTTON_SELECT = 35,
GAMEPAD_BUTTON_START = 36,
GAMEPAD_BUTTON_LPAD_CLICKED = 37,
GAMEPAD_BUTTON_LPAD_TOUCH = 38,
GAMEPAD_BUTTON_RPAD_CLICKED = 39,
GAMEPAD_BUTTON_RPAD_TOUCH = 40,
GAMEPAD_RIGHTSTICK_CLICK = 41,
GAMEPAD_RIGHTSTICK_TOUCH = 42,
GAMEPAD_LEFTSTICK_TOUCH = 43,
GAMEPAD_BUTTON_LBACK_UPPER = 44,
GAMEPAD_BUTTON_RBACK_UPPER = 45,
GAMEPAD_BUTTON_LAST = 46,
GAMEPAD_ANALOG_SCROLL = 47,
GAMEPAD_ANALOG_LEFT_KEYBOARD_CURSOR = 48,
GAMEPAD_ANALOG_RIGHT_KEYBOARD_CURSOR = 49,
GAMEPAD_ANALOG_LAST = 50
}
// TODO: Not the actual name, but the enum is only represented in a dropdown
// options vector, ty valve
export enum EThirdPartyControllerConfiguration {
Off,
DefaultSetting,
On,
}
export interface ActiveAccount {
strActiveAccountID: string;
strName: string;
strAvatarHash: string;
}
export interface ControllerInfo {
strName: string;
eControllerType: EControllerType;
nXInputIndex: number;
nControllerIndex: number;
eRumblePreference: EControllerRumbleSetting;
bWireless: boolean;
unUniqueID: number;
unVendorID: number;
unProductID: number;
/** Bitmask */
unCapabilities: number;
strFirmwareBuildTime: string;
strSerialNumber: string;
strChipID: string;
nLEDColorR: number;
nLEDColorG: number;
nLEDColorB: number;
flLEDBrightness: number;
flLEDSaturation: number;
nTurnOnSound: number;
nTurnOffSound: number;
nLStickDeadzone: number;
nRStickDeadzone: number;
nLHapticStrength: number;
nRHapticStrength: number;
flLPadPressureCurve: number;
flRPadPressureCurve: number;
bHaptics: boolean;
bSWAntiDrift: boolean;
flGyroStationaryTolerance: number;
flAccelerometerStationaryTolerance: number;
bRemoteDevice: boolean;
bNintendoLayout: boolean;
bUseReversedLayout: boolean;
ActiveAccount: ActiveAccount | undefined;
vecAltAccounts: any[]; // The type for this property might need to be more specific based on the actual data structure
}
export enum EControllerType {
None = -1,
Unknown,
UnknownSteamController,
SteamController, // Codename Gordon
SteamControllerV2, // Codename Headcrab
SteamControllerNeptune, // Steam Deck
FrontPanelBoard = 20,
Generic = 30,
XBox360Controller,
XBoxOneController,
PS3Controller,
PS4Controller,
WiiController,
AppleController,
AndroidController,
SwitchProController,
SwitchJoyConLeft,
SwitchJoyConRight,
SwitchJoyConPair,
SwitchProGenericInputOnlyController,
MobileTouch,
SwitchProXInputSwitchController,
PS5Controller,
XboxEliteController,
LastController, // Unverified
PS5EdgeController,
GenericKeyboard = 400,
GenericMouse = 800,
}
export interface ControllerStateChange {
unControllerIndex: number;
unPacketNum: number;
/**
* Bitmask representing pressed upper buttons.
* - Bit 0-8: Unknown (@todo Please provide more details if known)
* - Bit 9: L4
* - Bit 10: R4
* - Bit 11-13: Unknown (@todo Please provide more details if known)
* - Bit 14: Left Joystick Touch
* - Bit 15: Right Joystick Touch
* - Bit 16-17: Unknown (@todo Please provide more details if known)
* - Bit 18: Quick Access Menu
*/
ulUpperButtons: number;
/**
* Bitmask representing pressed buttons.
* - Bit 0: R2
* - Bit 1: L2
* - Bit 2: R1
* - Bit 3: L1
* - Bit 4: Y
* - Bit 5: B
* - Bit 6: X
* - Bit 7: A
* - Bit 8: D-Pad Up
* - Bit 9: D-Pad Right
* - Bit 10: D-Pad Left
* - Bit 11: D-Pad Down
* - Bit 12: Select
* - Bit 13: Steam/Home
* - Bit 14: Start
* - Bit 15: L5
* - Bit 16: R5
* - Bit 17: Left Touchpad Click
* - Bit 18: Right Touchpad Click
* - Bit 19: Left Touchpad Touch
* - Bit 20: Right Touchpad Touch
* - Bit 21: Unknown (@todo Please provide more details if known)
* - Bit 22: L3
* - Bit 23-25: Unknown (@todo Please provide more details if known)
* - Bit 26: R3
* - Bit 27-28: Unknown (@todo Please provide more details if known)
* - Bit 29: Mute (Dualsense)
* - Bit 30-31: Unknown (@todo Please provide more details if known)
*/
ulButtons: number;
sLeftPadX: number;
sLeftPadY: number;
sRightPadX: number;
sRightPadY: number;
sCenterPadX: number;
sCenterPadY: number;
sLeftStickX: number;
sLeftStickY: number;
sRightStickX: number;
sRightStickY: number;
sTriggerL: number;
sTriggerR: number;
flTrustedGravityVectorX: number;
flTrustedGravityVectorY: number;
flTrustedGravityVectorZ: number;
flSoftwareQuatW: number;
flSoftwareQuatX: number;
flSoftwareQuatY: number;
flSoftwareQuatZ: number;
flSoftwareGyroDegreesPerSecondPitch: number;
flSoftwareGyroDegreesPerSecondYaw: number;
flSoftwareGyroDegreesPerSecondRoll: number;
flHardwareQuatW: number;
flHardwareQuatX: number;
flHardwareQuatY: number;
flHardwareQuatZ: number;
flHardwareGyroDegreesPerSecondPitch: number;
flHardwareGyroDegreesPerSecondYaw: number;
flHardwareGyroDegreesPerSecondRoll: number;
flGyroNoiseLength: number;
flGyroCalibrationProgress: number;
flGravityVectorX: number;
flGravityVectorY: number;
flGravityVectorZ: number;
flAccelerometerNoiseLength: number;
sBatteryLevel: number;
sPressurePadLeft: number;
sPressurePadRight: number;
sPressureBumperLeft: number;
sPressureBumperRight: number;
unHardwareUpdateInMicrosec: number;
}
export interface GameKeyboardMessage {
m_bOpen: boolean;
nAppID: number;
m_dwPID: number;
m_dwOverlayPID: number;
m_hPipe: number;
/** @todo enum */
m_eInputMode: number;
/** @todo enum */
m_eLineInputMode: number;
m_pchDescription: string;
m_unCharMax: number;
m_pchExistingText: string;
}
export interface TouchMenuMessage {
bHasVirtualMenus: boolean;
unControllerIndex: number;
appID: number;
}

View File

@@ -0,0 +1,167 @@
import { Unregisterable } from "./shared";
import {EAppUpdateError} from "./App";
/**
* Represents functions related to Steam Install Folders.
*/
export interface InstallFolder {
/**
* Adds a Steam Library folder to the Steam client.
* @param path The path of the Steam Library folder to be added.
* @returns the index of the added folder.
*/
AddInstallFolder(path: string): Promise<number>;
/**
* Opens the file explorer to browse files in a specific Steam Library folder.
* @param folderIndex The index of the folder to be opened.
*/
BrowseFilesInFolder(folderIndex: number): void;
/**
* Cancels the current move operation for moving game content.
*/
CancelMove(): void;
/**
* Retrieves a list of installed Steam Library folders.
* @returns an array of SteamInstallFolder objects.
*/
GetInstallFolders(): Promise<SteamInstallFolder[]>;
/**
* Retrieves a list of potential Steam Library folders that can be added.
* @returns an array of PotentialInstallFolder objects.
*/
GetPotentialFolders(): Promise<PotentialInstallFolder[]>;
/**
* Moves the installation folder for a specific app to another Steam Library folder.
* @param appId The ID of the application to be moved.
* @param folderIndex The index of the target Steam Library folder.
*/
MoveInstallFolderForApp(appId: number, folderIndex: number): void;
/**
* Refreshes the list of installed Steam Library folders.
*/
RefreshFolders(): void;
/**
* Registers a callback function to be called when changes occur in Steam Install Folders.
* @param callback The callback function to be called.
* @returns an object that can be used to unregister the callback.
*/
RegisterForInstallFolderChanges(callback: (change: FolderChange) => void): Unregisterable;
/**
* Registers a callback function to be called when moving game content progresses.
* @param callback The callback function to be called.
* @returns an object that can be used to unregister the callback.
*/
RegisterForMoveContentProgress(callback: (progress: MoveContentProgress) => void): Unregisterable;
/**
* Registers a callback function to be called when repairing an install folder is finished.
* @param callback The callback function to be called.
* @returns an object that can be used to unregister the callback.
*/
RegisterForRepairFolderFinished(callback: (change: FolderChange) => void): Unregisterable;
/**
* Removes a Steam Library folder from the Steam client.
* @param folderIndex The index of the folder to be removed.
*/
RemoveInstallFolder(folderIndex: number): void;
/**
* Repairs an installed Steam Library folder.
* @param folderIndex The index of the folder to be repaired.
*/
RepairInstallFolder(folderIndex: number): void;
/**
* Sets a specific Steam Library folder as the default install folder.
* @param folderIndex The index of the folder to be set as default.
*/
SetDefaultInstallFolder(folderIndex: number): void;
/**
* Sets a user-defined label for a specific Steam Library folder.
* @param folderIndex The index of the folder to be labeled.
* @param label The label to be assigned to the folder.
*/
SetFolderLabel(folderIndex: number, label: string): void;
}
/**
* Represents information about an installation folder.
*/
export interface SteamInstallFolder extends PotentialInstallFolder {
/** Index of the folder. */
nFolderIndex: number;
/** Used space in the folder. */
strUsedSize: string;
/** Size of DLC storage used in the folder. */
strDLCSize: string;
/** Size of workshop storage used in the folder. */
strWorkshopSize: string;
/** Size of staged storage used in the folder. */
strStagedSize: string;
/** Indicates if the folder is set as the default installation folder. */
bIsDefaultFolder: boolean;
/** Indicates if the folder is currently mounted. */
bIsMounted: boolean;
/** List of applications installed in the folder. */
vecApps: AppInfo[];
}
export interface PotentialInstallFolder {
/** Path of the folder. */
strFolderPath: string;
/** User label for the folder. */
strUserLabel: string;
/** Name of the drive where the folder is located. */
strDriveName: string;
/** Total capacity of the folder. */
strCapacity: string;
/** Available free space in the folder. */
strFreeSpace: string;
/** Indicates if the folder is on a fixed drive. */
bIsFixed: boolean;
}
/**
* Represents information about an installed application.
*/
export interface AppInfo {
/** ID of the application. */
nAppID: number;
/** Name of the application. */
strAppName: string;
/** Sorting information for the application. */
strSortAs: string;
/** Last played time in Unix Epoch time format. */
rtLastPlayed: number;
/** Size of used storage by the application. */
strUsedSize: string;
/** Size of DLC storage used by the application. */
strDLCSize: string;
/** Size of workshop storage used by the application. */
strWorkshopSize: string;
/** Size of staged storage used by the application. */
strStagedSize: string;
}
export interface FolderChange {
folderIndex: number;
}
export interface MoveContentProgress {
appid: number;
eError: EAppUpdateError;
flProgress: number;
strBytesMoved: string;
strTotalBytesToMove: string;
nFilesMoved: number;
}

View File

@@ -0,0 +1,131 @@
import { Unregisterable } from "./shared";
import {EAppUpdateError} from "./App";
/**
* Represents functions related to managing installs and installation wizards in Steam.
*/
export interface Installs {
/**
* Cancels the installation wizard if it is open.
*/
CancelInstall(): void;
/**
* Continues and starts the installation if the wizard is still open.
*/
ContinueInstall(): void;
/**
* Retrieves information from the last opened or currently opened installation wizard.
*/
GetInstallManagerInfo(): Promise<InstallMgrInfo>;
/**
* Opens the restore from backup installer wizard for a specific app.
* @param appBackupPath The backup path of the app.
*/
OpenInstallBackup(appBackupPath: string): void;
/**
* Opens the installation wizard for specified app IDs.
* @param appIds An array of app IDs to install.
*/
OpenInstallWizard(appIds: number[]): void;
/**
* Opens the uninstall wizard for specified app IDs.
* @param appIds An array of app IDs to uninstall.
* @param dontPrompt Whether to *not* prompt the user to uninstall.
*/
OpenUninstallWizard(appIds: number[], dontPrompt: boolean): void;
RegisterForShowConfirmUninstall: Unregisterable; // Broken? doesn't seem to work
/**
* Registers a callback function to be called when the "Failed Uninstall" dialog is shown.
* @param callback The callback function to be called when the dialog is shown.
* @returns an object that can be used to unregister the callback.
*/
RegisterForShowFailedUninstall(callback: (appId: number, reason: EAppUpdateError) => void): Unregisterable;
/**
* Registers a callback function to be called when the installation wizard is shown.
* @param callback The callback function to be called.
* @returns an object that can be used to unregister the callback.
*/
RegisterForShowInstallWizard(callback: (data: InstallMgrInfo) => void): Unregisterable;
RegisterForShowRegisterCDKey: any;
/**
* Sets a list of app identifiers for downloads in the installation wizard.
* @param appIds An array of app IDs to set.
* @remarks The wizard will not reflect this change immediately, but changing another option will.
*/
SetAppList(appIds: number[]): void;
/**
* Sets the options for creating shortcuts in the installation wizard.
* @param bDesktopShortcut Whether to create a desktop shortcut.
* @param bSystemMenuShortcut Whether to create a system menu shortcut.
* @remarks The wizard will not reflect this change immediately, but changing another option will.
*/
SetCreateShortcuts(bDesktopShortcut: boolean, bSystemMenuShortcut: boolean): void;
/**
* Sets the install folder for the installation wizard using an install folder index.
* @param folderIndex The index of the install folder.
* @remarks The wizard will not reflect this change immediately, but changing another option will.
*/
SetInstallFolder(folderIndex: number): void;
}
export interface InstallMgrInfo {
rgAppIDs: InstallInfoApps[];
eInstallState: EInstallMgrState;
nDiskSpaceRequired: number;
nDiskSpaceAvailable: number;
nCurrentDisk: number;
nTotalDisks: number;
bCanChangeInstallFolder: boolean;
/**
* Index of the install folder. -1 if not installed.
*/
iInstallFolder: number;
iUnmountedFolder: number;
currentAppID: number;
eAppError: EAppUpdateError;
errorDetail: string;
bSystemMenuShortcut: boolean;
bDesktopShortcut: boolean;
bIsBackupInstall: boolean;
// device/hostname
strPeerContentServer: string;
bPeerContentServerOnline: boolean;
bPeerContentServerAvailable: boolean;
}
export interface InstallInfoApps {
nAppID: number;
lDiskSpaceRequiredBytes: number;
}
export enum EInstallMgrState {
None,
Setup,
WaitLicense,
FreeLicense,
ShowCDKey,
WaitAppInfo,
ShowPassword,
ShowConfig,
ShowEULAs,
CreateApps,
ReadFromMedia,
ShowChangeMedia,
WaitLegacyCDKeys,
ShowSignup,
Complete,
Failed,
Canceled,
}

View File

@@ -0,0 +1,20 @@
import { Unregisterable } from "./shared";
export interface Messaging {
// section - "ContentManagement", "JumpList", "PostToLibrary"
// seems multipurpose
RegisterForMessages<T extends string>(
message: T,
callback: (message: T, section: string, args: string) => void,
): Unregisterable;
/*
function m(e) {
SteamClient.Messaging.PostMessage("LibraryCommands", "ShowFriendChatDialog", JSON.stringify({
steamid: e.persona.m_steamid.ConvertTo64BitString()
}))
}
SteamClient.Messaging.PostMessage("FriendsUI", "AcceptedRemotePlayInvite", JSON.stringify({id: this.appID})) : SteamClient.Messaging.PostMessage("FriendsUI", "AcceptedGameInvite", JSON.stringify({id: this.appID}))
*/
PostMessage(message: string, section: string, args: string): void;
}

View File

@@ -0,0 +1,99 @@
import { Unregisterable } from "./shared";
/**
* Represents functions related to controlling music playback in the Steam client.
*/
export interface Music {
/**
* Decreases the music volume by 10%.
*/
DecreaseVolume(): void;
/**
* Increases the music volume by 10%.
*/
IncreaseVolume(): void;
/**
* Plays the next track in the music playlist.
*/
PlayNext(): void;
/**
* Plays the previous track in the music playlist.
*/
PlayPrevious(): void;
/**
* Registers a callback function to be called when music playback changes.
* @param callback The callback function to be called.
* @returns an object that can be used to unregister the callback.
*/
RegisterForMusicPlaybackChanges(callback: (param0: boolean | MusicTrack) => void): Unregisterable;
/**
* Registers a callback function to be called when the music playback position changes.
* @param callback The callback function to be called.
* @returns an object that can be used to unregister the callback.
*/
RegisterForMusicPlaybackPosition(callback: (position: number) => void): Unregisterable;
/**
* Sets the playback position of the music track.
* @param position The position to set in seconds.
*/
SetPlaybackPosition(position: number): void;
/**
* Sets the repeat status for music playback.
* @param status The repeat status.
*/
SetPlayingRepeatStatus(status: EMusicPlayingRepeatStatus): void;
/**
* Sets the shuffle status for music playback.
* @param value True to enable shuffle, false to disable shuffle.
*/
SetPlayingShuffled(value: boolean): void;
/**
* Sets the volume for music playback.
* @param volume The volume level to set.
* @remarks Ranges from 0 to 100.
*/
SetVolume(volume: number): void;
/**
* Toggles the mute state of the music volume.
*/
ToggleMuteVolume(): void;
/**
* Toggles between play and pause for music playback.
*/
TogglePlayPause(): void;
}
export interface MusicTrack {
uSoundtrackAppId: number;
ePlaybackStatus: EAudioPlayback;
eRepeatStatus: EMusicPlayingRepeatStatus;
bShuffle: boolean;
nVolume: number;
nActiveTrack: number;
nLengthInMsec: number;
}
export enum EAudioPlayback {
Undefined,
Playing,
Paused,
Idle,
}
export enum EMusicPlayingRepeatStatus {
None,
All,
Once,
Max,
}

View File

@@ -0,0 +1,333 @@
import { JsPbMessage, Unregisterable } from "./shared";
/**
* Everything is taken from here:
* https://github.com/SteamDatabase/SteamTracking/blob/master/Protobufs/steammessages_clientnotificationtypes.proto
*/
export interface Notifications {
/**
* If `data` is deserialized, returns one of the following here: {@link Notifications}
* @returns an object that can be used to unregister the callback.
*/
RegisterForNotifications(
callback: (notificationIndex: number, type: EClientNotificationType, data: ArrayBuffer) => void,
): Unregisterable;
}
export enum EClientNotificationType {
Invalid,
DownloadComplete,
FriendInvite,
FriendInGame,
FriendOnline,
Achievement,
LowBattery,
SystemUpdate,
FriendMessage,
GroupChatMessage,
FriendInviteRollup,
FamilySharingDeviceAuthorizationChanged,
FamilySharingStopPlaying,
FamilySharingLibraryAvailable,
Screenshot,
CloudSyncFailure,
CloudSyncConflict,
IncomingVoiceChat,
ClaimSteamDeckRewards,
GiftReceived,
ItemAnnouncement,
HardwareSurvey,
LowDiskSpace,
BatteryTemperature,
DockUnsupportedFirmware,
PeerContentUpload,
CannotReadControllerGuideButton,
Comment,
Wishlist,
TradeOffer,
AsyncGame,
General,
HelpRequest,
OverlaySplashScreen,
BroadcastAvailableToWatch,
TimedTrialRemaining,
LoginRefresh,
MajorSale,
TimerExpired,
ModeratorMsg,
SteamInputActionSetChanged,
RemoteClientConnection,
RemoteClientStartStream,
StreamingClientConnection,
FamilyInvite,
PlaytimeWarning,
FamilyPurchaseRequest,
FamilyPurchaseRequestResponse,
ParentalFeatureRequest,
ParentalPlaytimeRequest,
GameRecordingError,
ParentalFeatureResponse,
ParentalPlaytimeResponse,
RequestedGameAdded,
ClipDownloaded,
GameRecordingStart,
GameRecordingStop,
GameRecordingUserMarkerAdded,
GameRecordingInstantClip,
}
export enum ESystemUpdateNotificationType {
Invalid,
Available,
NeedsRestart,
}
export enum EGameRecordingErrorType {
General = 1,
LowDiskSpace,
}
export interface ClientNotificationGroupChatMessage extends JsPbMessage {
tag(): string;
/** A Steam64 ID. */
steamid_sender(): string;
chat_group_id(): string;
chat_id(): string;
title(): string;
body(): string;
rawbody(): string;
icon(): string;
notificationid(): number;
}
export interface ClientNotificationFriendMessage extends JsPbMessage {
body(): string;
icon(): string;
notificationid(): number;
response_steamurl(): string;
/** A Steam64 ID. */
steamid(): string;
tag(): string;
title(): string;
}
export interface ClientNotificationCloudSyncFailure extends JsPbMessage {
appid(): number;
}
export interface ClientNotificationCloudSyncConflict extends JsPbMessage {
appid(): number;
}
export interface ClientNotificationScreenshot extends JsPbMessage {
screenshot_handle(): string;
description(): string;
local_url(): string;
}
export interface ClientNotificationDownloadCompleted extends JsPbMessage {
appid(): number;
dlc_appid(): number;
}
export interface ClientNotificationFriendInvite extends JsPbMessage {
steamid(): number;
}
export interface ClientNotificationFriendInviteRollup extends JsPbMessage {
new_invite_count(): number;
}
export interface ClientNotificationFriendInGame extends JsPbMessage {
steamid(): number;
game_name(): string;
}
export interface ClientNotificationFriendOnline extends JsPbMessage {
steamid(): number;
}
export interface ClientNotificationAchievement extends JsPbMessage {
achievement_id(): string;
appid(): number;
name(): string;
description(): string;
image_url(): string;
achieved(): boolean;
rtime_unlocked(): number;
min_progress(): number;
current_progress(): number;
max_progress(): number;
global_achieved_pct(): number;
}
export interface ClientNotificationLowBattery extends JsPbMessage {
pct_remaining(): number;
}
export interface ClientNotificationSystemUpdate extends JsPbMessage {
type(): ESystemUpdateNotificationType;
}
export interface ClientNotificationFriendMessage extends JsPbMessage {
tag(): string;
steamid(): string;
title(): string;
body(): string;
icon(): string;
notificationid(): number;
response_steamurl(): string;
}
export interface ClientNotificationGroupChatMessage extends JsPbMessage {
tag(): string;
steamid_sender(): string;
chat_group_id(): string;
chat_id(): string;
title(): string;
body(): string;
rawbody(): string;
icon(): string;
notificationid(): number;
}
export interface ClientNotificationFamilySharingDeviceAuthorizationChanged extends JsPbMessage {
accountid_owner(): number;
authorized(): boolean;
}
export interface ClientNotificationFamilySharingStopPlaying extends JsPbMessage {
accountid_owner(): number;
seconds_remaining(): number;
appid(): number;
}
export interface ClientNotificationFamilySharingLibraryAvailable extends JsPbMessage {
accountid_owner(): number;
}
export interface ClientNotificationIncomingVoiceChat extends JsPbMessage {
steamid(): number;
}
export interface ClientNotificationClaimSteamDeckRewards extends JsPbMessage {
}
export interface ClientNotificationGiftReceived extends JsPbMessage {
sender_name(): string;
}
export interface ClientNotificationItemAnnouncement extends JsPbMessage {
new_item_count(): number;
new_backpack_items(): boolean;
}
export interface ClientNotificationHardwareSurveyPending extends JsPbMessage {
}
export interface ClientNotificationLowDiskSpace extends JsPbMessage {
folder_index(): number;
}
export interface ClientNotificationBatteryTemperature extends JsPbMessage {
temperature(): number;
notification_type(): string;
}
export interface ClientNotificationDockUnsupportedFirmware extends JsPbMessage {
}
export interface ClientNotificationPeerContentUpload extends JsPbMessage {
appid(): number;
peer_name(): string;
}
export interface ClientNotificationCannotReadControllerGuideButton extends JsPbMessage {
controller_index(): number;
}
export interface ClientNotificationOverlaySplashScreen extends JsPbMessage {
}
export interface ClientNotificationBroadcastAvailableToWatch extends JsPbMessage {
broadcast_permission(): number;
}
export interface ClientNotificationTimedTrialRemaining extends JsPbMessage {
appid(): number;
icon(): string;
offline(): boolean;
allowed_seconds(): number;
played_seconds(): number;
}
export interface ClientNotificationLoginRefresh extends JsPbMessage {
}
export interface ClientNotificationTimerExpired extends JsPbMessage {
}
export interface ClientNotificationSteamInputActionSetChanged extends JsPbMessage {
controller_index(): number;
action_set_name(): string;
}
export interface ClientNotificationRemoteClientConnection extends JsPbMessage {
machine(): string;
connected(): boolean;
}
export interface ClientNotificationRemoteClientStartStream extends JsPbMessage {
machine(): string;
game_name(): string;
}
export interface ClientNotificationStreamingClientConnection extends JsPbMessage {
hostname(): string;
machine(): string;
connected(): boolean;
}
export interface ClientNotificationPlaytimeWarning extends JsPbMessage {
type(): string;
playtime_remaining(): number;
}
export interface ClientNotificationGameRecordingError extends JsPbMessage {
game_id(): number;
error_type(): EGameRecordingErrorType;
}
export interface ClientNotificationGameRecordingStart extends JsPbMessage {
game_id(): number;
}
export interface ClientNotificationGameRecordingStop extends JsPbMessage {
game_id(): number;
clip_id(): string;
duration_secs(): number;
}
export interface ClientNotificationGameRecordingUserMarkerAdded extends JsPbMessage {
game_id(): number;
}
export interface CClientNotificationGameRecordingInstantClip extends JsPbMessage {
game_id(): number;
clip_id(): string;
duration_secs(): number;
}

View File

@@ -0,0 +1,122 @@
import { Unregisterable } from "./shared";
export interface OpenVR {
Device: VRDevice;
DeviceProperties: DeviceProperties;
/**
* @throws OperationResponse if mutual capabilities haven't been loaded.
*/
GetMutualCapabilities(): Promise<any>;
GetWebSecret(): Promise<string>;
InstallVR(): any;
Keyboard: Keyboard;
PathProperties: PathProperties;
QuitAllVR(): any;
RegisterForButtonPress: Unregisterable;
RegisterForHMDActivityLevelChanged(callback: (m_eHMDActivityLevel: EHMDActivityLevel) => void): Unregisterable;
RegisterForInstallDialog: Unregisterable;
RegisterForStartupErrors(callback: (clientError: any, initError: any, initErrorString: string) => void): Unregisterable;
RegisterForVRHardwareDetected(callback: (m_bHMDPresent: any, m_bHMDHardwareDetected: any, m_strHMDName: any) => void): Unregisterable;
RegisterForVRModeChange(callback: (m_bIsVRRunning: boolean) => void): Unregisterable;
RegisterForVRSceneAppChange(callback: (param0: number) => void): Unregisterable;
SetOverlayInteractionAffordance: any;
StartVR: any;
TriggerOverlayHapticEffect: any;
VRNotifications: VRNotifications;
VROverlay: VROverlay;
}
export interface VRDevice {
BIsConnected: any;
RegisterForDeviceConnectivityChange: Unregisterable;
RegisterForVRDeviceSeenRecently(callback: (m_bVRDeviceSeenRecently: any) => void): Unregisterable;
}
export interface DeviceProperties {
GetBoolDeviceProperty: any;
GetDoubleDeviceProperty: any;
GetFloatDeviceProperty: any;
GetInt32DeviceProperty: any;
GetStringDeviceProperty: any;
RegisterForDevicePropertyChange: Unregisterable;
}
export interface Keyboard {
Hide(): any;
/**
* {@link EKeyboardFlags} could be useful here
*/
RegisterForStatus(callback: (m_bIsKeyboardOpen: boolean, m_eKeyboardFlags: number, m_sInitialKeyboardText: string) => void): Unregisterable;
SendDone(): any;
SendText(key: string): any; //???
Show(): any;
}
export interface PathProperties {
GetBoolPathProperty: any;
GetDoublePathProperty: any;
GetFloatPathProperty: any;
GetInt32PathProperty: any;
GetStringPathProperty: any;
RegisterForPathPropertyChange: any;
SetBoolPathProperty: any;
SetDoublePathProperty: any;
SetFloatPathProperty: any;
SetInt32PathProperty: any;
SetStringPathProperty: any;
}
export interface VRNotifications {
HideCustomNotification: any;
RegisterForNotificationEvent: Unregisterable;
ShowCustomNotification: any;
}
export interface VROverlay {
HideDashboard: any;
IsDashboardVisible(): Promise<boolean>;
RegisterForButtonPress: Unregisterable;
RegisterForCursorMovement: Unregisterable;
RegisterForThumbnailChanged: Unregisterable;
RegisterForVisibilityChanged: Unregisterable;
ShowDashboard: any;
SwitchToDashboardOverlay(param0: string): void;
}
export enum EHMDActivityLevel {
Unknown = -1,
Idle,
UserInteraction,
UserInteraction_Timeout,
Standby,
Idle_Timeout,
}
export enum EKeyboardFlags {
Minimal = 1 << 0,
Modal = 1 << 1,
ShowArrowKeys = 1 << 2,
HideDoneKey = 1 << 3,
}

View File

@@ -0,0 +1,172 @@
import type { EBrowserType, ESteamRealm, EUIComposition, EUIMode, Unregisterable } from "./shared";
export interface Overlay {
/**
* Destroys the gamepad UI desktop configurator window if open.
*/
DestroyGamePadUIDesktopConfiguratorWindow(): void;
GetOverlayBrowserInfo(): Promise<OverlayBrowserInfo[]>;
// steam://gamewebcallback
HandleGameWebCallback(url: string): void;
/**
* @param protocol Something like {@link OverlayBrowserProtocols.strScheme}
*/
HandleProtocolForOverlayBrowser(appId: number, protocol: string): void;
/**
* Registers a callback function to be called when an overlay is activated from an app.
* @param callback The callback function to be called.
* @returns an object that can be used to unregister the callback.
*/
RegisterForActivateOverlayRequests(callback: (request: ActivateOverlayRequest) => void): Unregisterable;
/**
* Registers a callback function to be called when a microtransaction authorization is requested.
* @param callback The callback function to be called.
* @returns an object that can be used to unregister the callback.
*/
RegisterForMicroTxnAuth(
callback: (appId: number, microTxnId: string, realm: ESteamRealm, microTxnUrl: string) => void,
): Unregisterable;
/**
* Registers a callback function to be called when a microtransaction authorization is dismissed by the user in Steam's authorization page.
* @param callback The callback function to be called.
* @returns an object that can be used to unregister the callback.
*/
RegisterForMicroTxnAuthDismiss(callback: (appId: number, microTxnId: string) => void): Unregisterable;
RegisterForNotificationPositionChanged(
callback: (appId: number, position: ENotificationPosition, horizontalInset: number, verticalInset: number) => void,
): Unregisterable;
/**
* Registers a callback function to be called when an overlay is activated or closed.
* @param callback The callback function to be called.
* @returns an object that can be used to unregister the callback.
*/
RegisterForOverlayActivated(
callback: (overlayProcessPid: number, appId: number, active: boolean, param3: boolean) => void,
): Unregisterable;
/**
* Registers a callback function to be called when the overlay browser protocols change.
* @param callback The callback function to be called.
* @returns an object that can be used to unregister the callback.
*/
RegisterForOverlayBrowserProtocols(
callback: (browseProtocols: OverlayBrowserProtocols) => void,
): Unregisterable;
/**
* Registers **the** callback function to be called when the overlay browser information changes.
* @param callback The callback function to be called when the overlay browser information changes.
* @returns an object that can be used to unregister the callback.
* @remarks Do Not Use, this will break the overlay unless you know what you are doing.
*/
RegisterOverlayBrowserInfoChanged(callback: () => void): Unregisterable;
SetOverlayState(appId: string, state: EUIComposition): void;
}
type OverlayRequestDialog_t =
| 'achievements'
| 'asyncnotificationsrequested'
| 'chat'
| 'community'
| 'friendadd'
| 'friendremove'
| 'friendrequestaccept'
| 'friendrequestignore'
| 'friendremove'
| 'jointrade'
| 'leaderboards'
| 'lobbyinvite'
| 'lobbyinviteconnectstring'
| 'officialgamegroup'
| 'requestplaytime'
| 'remoteplaytogether'
| 'remoteplaytogetherinvite'
| 'settings'
| 'stats'
| 'steamid'
| 'store';
// EPosition
export enum ENotificationPosition {
TopLeft,
TopRight,
BottomLeft,
BottomRight,
}
export interface ActivateOverlayRequest {
/**
* The app ID that just had an overlay request.
*/
appid: number;
/**
* `true` if webpage, and so {@link strDialog} will start with `https://`.
*/
bWebPage: boolean;
eFlag: EOverlayToStoreFlag;
eWebPageMode: EActivateGameOverlayToWebPageMode;
/**
* Steam64 ID.
*/
steamidTarget: string;
/**
* Game invites string for `Friends.InviteUserToGame`.
*/
strConnectString: string;
/**
* Web page URL if starts with `https://`, so cast the type to `string` if it is.
*/
strDialog: OverlayRequestDialog_t;
/**
* App ID of the requesting game.
*/
unRequestingAppID: number;
}
export interface OverlayBrowserInfo {
appID: number;
eBrowserType: EBrowserType;
eUIMode: EUIMode;
flDisplayScale?: number;
gameID: string;
nBrowserID: number;
nScreenHeight: number;
nScreenWidth: number;
/**
* The PID of the overlay process.
*/
unPID: number;
}
export interface OverlayBrowserProtocols {
unAppID: number;
strScheme: string;
bAdded: boolean;
}
export enum EActivateGameOverlayToWebPageMode {
Default,
Modal,
}
export enum EOverlayToStoreFlag {
None,
AddToCart,
AddToCartAndShow,
}

View File

@@ -0,0 +1,129 @@
import { EResult, Unregisterable } from "./shared";
/**
* Interface for managing parental control settings.
*/
export interface Parental {
/**
* Locks the parental control settings.
*/
LockParentalLock(): void;
RegisterForParentalPlaytimeWarnings(callback: (time: number) => void): Unregisterable;
/**
* Registers a callback function to be invoked when parental settings change.
* @param callback The callback function to be invoked when parental settings change.
* @returns an object that can be used to unregister the callback.
*/
RegisterForParentalSettingsChanges(callback: (settings: ParentalSettings) => void): Unregisterable;
/**
* Unlocks the parental lock with the provided PIN.
* @param pin The 4-digit PIN to unlock the parental lock.
* @returns a number representing the result of the unlock operation.
*/
UnlockParentalLock(pin: string, param1: boolean): Promise<EResult>;
}
export interface ParentalSettings {
ever_enabled: boolean;
locked: boolean;
/**
* If deserialized, returns {@link ParentalSettingsProtoMsg}.
*/
settings: ArrayBuffer;
strPlaintextPassword: string;
}
/**
* Represents the parental settings and restrictions.
* @todo This whole thing is unconfirmed as I do not have access to parental
* stuff and things
*/
export interface ParentalSettingsProtoMsg {
steamid?: number;
applist_base_id?: number;
applist_base_description?: string;
/**
* Base list.
*/
applist_base: ParentalApp[];
/**
* Custom list of allowed applications.
*/
applist_custom: ParentalApp[];
/**
* @todo enum ?
*/
passwordhashtype?: number;
salt?: number;
passwordhash?: number;
/**
* Indicates whether parental settings are enabled.
*/
is_enabled?: boolean;
/**
* Bitmask representing enabled features.
* - Bit 0: Unknown (@todo Please provide more details if known)
* - Bit 1: Online content & features - Steam Store
* - Bit 2: Online content & features - Community-generated content
* - Bit 3: Online content & features - My online profile, screenshots, and achievements
* - Bit 4: Online content & features - Friends, chat, and groups
* - Bit 5-11: Unknown (@todo Please provide more details if known)
* - Bit 12: Library content - 0: Only games I choose, 1: All games
* @todo {@link EParentalFeature} ?
*/
enabled_features?: number;
/**
* Email for recovery (if applicable).
*/
recovery_email?: string;
is_site_license_lock?: boolean;
temporary_enabled_features?: number;
rtime_temporary_feature_expiration?: number;
playtime_restrictions?: ParentalPlaytimeRestrictions;
temporary_playtime_restrictions?: ParentalTemporaryPlaytimeRestrictions;
excluded_store_content_descriptors: number[];
excluded_community_content_descriptors: number[];
utility_appids: number[];
}
interface ParentalApp {
appid: number;
is_allowed: boolean;
}
interface ParentalPlaytimeDay {
allowed_time_windows?: number;
allowed_daily_minutes?: number;
}
interface ParentalPlaytimeRestrictions {
apply_playtime_restrictions?: boolean;
playtime_days: ParentalPlaytimeDay[];
}
interface ParentalTemporaryPlaytimeRestrictions {
restrictions?: ParentalPlaytimeDay;
rtime_expires?: number;
}
export enum EParentalFeature {
Invalid,
Store,
Community,
Profile,
Friends,
News,
Trading,
Settings,
Console,
Browser,
ParentalSetup,
Library,
Test,
SiteLicense,
KioskMode,
Max,
}

View File

@@ -0,0 +1,342 @@
import {EControllerType} from "./Input";
import { EParentalFeature } from "./Parental";
import { EResult, Unregisterable } from "./shared";
export interface RemotePlay {
/**
* @param param1 TODO: Something about restrictions countries ? maybe it's games
*/
BCanAcceptInviteForGame(gameId: string, param1: string): Promise<boolean>;
BCanCreateInviteForGame(gameId: string, param1: boolean): Promise<boolean>;
BRemotePlayTogetherGuestOnPhoneOrTablet(steam64Id: string, guestId: number): Promise<boolean>;
BRemotePlayTogetherGuestSupported(): Promise<boolean>;
// TODO: both calls have 1 arg, but it requires 2
CancelInviteAndSession(steam64Id: string, param1: number): Promise<EResult>;
CancelInviteAndSessionWithGuestID(steam64Id: string, guestId: number): Promise<EResult>;
CancelRemoteClientPairing(): void;
CloseGroup(): Promise<number>;
CreateGroup(param0: string): Promise<EResult>;
CreateInviteAndSession(steam64Id: string, param1: string): Promise<EResult>;
CreateInviteAndSessionWithGuestID(steam64Id: string, guestId: number, param2: string): Promise<EResult>;
GetClientID(): Promise<string>;
// TODO: -1 no preference? no idea where the settings are anyway lol
GetClientStreamingBitrate(): Promise<number>;
GetClientStreamingQuality(): Promise<number>;
GetControllerType(controllerIndex: number): Promise<EControllerType>;
/**
* @returns an integer from 0 to 100.
*/
GetGameSystemVolume(): Promise<number>;
GetPerUserInputSettings(steam64Id: string): Promise<RemotePlayInputSettings>;
GetPerUserInputSettingsWithGuestID(steam64Id: string, guestId: number): Promise<RemotePlayInputSettings>;
IdentifyController(nControllerIndex: number): void;
InstallAudioDriver(): void;
InstallInputDriver(): void;
MoveControllerToSlot(controllerIndex: number, slot: number): void;
RegisterForAdditionalParentalBlocks(callback: (blocks: EParentalFeature[]) => void): Unregisterable;
RegisterForAudioDriverPrompt(callback: () => void): Unregisterable;
/**
* @todo no mentions of it in Steam code
*/
RegisterForBitrateOverride: Unregisterable;
RegisterForClearControllers(callback: () => void): Unregisterable;
RegisterForControllerIndexSet(
callback: (steamid: string, slot: number, guestid: number) => void
): Unregisterable;
RegisterForDevicesChanges(callback: (devices: RemotePlayDevice[]) => void): Unregisterable;
RegisterForGroupCreated(callback: (steamId: string, appId: string) => void): Unregisterable;
RegisterForGroupDisbanded(callback: () => void): Unregisterable;
RegisterForInputDriverPrompt(callback: () => void): Unregisterable;
RegisterForInputDriverRestartNotice(callback: () => void): Unregisterable;
RegisterForInputUsed(
callback: (steam64Id: string, type: EClientUsedInputType, guestId: number) => void,
): Unregisterable; // only fires on host
RegisterForInviteResult(
callback: (
steamId: string,
param1: any,
result: ERemoteClientLaunch,
) => void
): Unregisterable;
RegisterForNetworkUtilizationUpdate(
callback: (steam64Id: string, guestId: number, networkUtilization: number, networkDuration: number) => void,
): Unregisterable; // only fires on host
RegisterForPlaceholderStateChanged(callback: (isShowingPlaceholder: boolean) => void): Unregisterable;
RegisterForPlayerInputSettingsChanged(
callback: (steamId: string, settings: RemotePlayInputSettings, guestId: number) => void
): Unregisterable;
RegisterForQualityOverride(callback: (hostStreamingQualityOverride: number) => void): Unregisterable;
RegisterForRemoteClientLaunchFailed(callback: (state: ERemoteClientLaunch) => void): Unregisterable;
RegisterForRemoteClientStarted(callback: (steam64Id: string, appId: string) => void): Unregisterable; // only fires on client
RegisterForRemoteClientStopped(callback: (steam64Id: string, appId: string) => void): Unregisterable; // only fires on client
RegisterForRemoteDeviceAuthorizationCancelled(callback: () => void): Unregisterable;
RegisterForRemoteDeviceAuthorizationRequested(callback: (device: string) => void): Unregisterable;
RegisterForRemoteDevicePairingPINChanged(callback: (device: string, pin: string) => void): Unregisterable;
RegisterForRestrictedSessionChanges(callback: (restrictedSession: boolean) => void): Unregisterable;
RegisterForSessionStopped(callback: (steam64Id: string, guestId: number, avatarHash: string) => void): Unregisterable;
RegisterForSessionStarted(callback: (steam64Id: string, gameId: string, guestId: number) => void): Unregisterable;
RegisterForSessionStopped(callback: (steam64Id: string, guestId: number) => void): Unregisterable;
RegisterForSettingsChanges(callback: (remotePlaySettings: RemotePlaySettings) => void): Unregisterable;
SetClientStreamingBitrate(bitrate: number): void;
SetClientStreamingQuality(quality: number): void;
SetGameSystemVolume(volume: number): void;
SetPerUserControllerInputEnabled(steam64Id: string, enabled: boolean): void;
SetPerUserControllerInputEnabledWithGuestID(steam64Id: string, guestId: number, enabled: boolean): void;
SetPerUserKeyboardInputEnabled(steam64Id: string, enabled: boolean): void;
SetPerUserKeyboardInputEnabledWithGuestID(steam64Id: string, guestId: number, enabled: boolean): void;
SetPerUserMouseInputEnabled(steam64Id: string, enabled: boolean): void;
SetPerUserMouseInputEnabledWithGuestID(steam64Id: string, guestId: number, enabled: boolean): void;
SetRemoteDeviceAuthorized(param0: boolean, param1: string): void;
SetRemoteDevicePIN(pin: string): void;
SetRemotePlayEnabled(enabled: boolean): void;
/**
* @param base64 Serialized base64 message from {@link StreamingClientConfig}.
*/
SetStreamingClientConfig(base64: string, sessionId: number): void;
/**
* Enables advanced client options.
*/
SetStreamingClientConfigEnabled(value: boolean): void;
SetStreamingDesktopToRemotePlayTogetherEnabled(enabled: boolean): void;
SetStreamingP2PScope(scope: EStreamP2PScope): void;
/**
* @param base64 Serialized base64 message from {@link StreamingServerConfig}.
*/
SetStreamingServerConfig(base64: string, sessionId: number): void;
/**
* Enables advanced host options.
*/
SetStreamingServerConfigEnabled(value: boolean): void;
StopStreamingClient(): void;
StopStreamingSession(id: number): void;
StopStreamingSessionAndSuspendDevice(id: number): void;
UnlockH264(): void;
/**
* Unpairs all devices.
*/
UnpairRemoteDevices(): void;
}
export enum EClientUsedInputType {
Keyboard,
Mouse,
Controller,
Max,
}
export interface RemotePlayDevice {
clientId: string;
clientName: string;
status: string; // "Connected", "Paired",
formFactor: number;
unStreamingSessionID: number;
bCanSteamVR: boolean;
bCanSuspend: boolean;
}
interface RemotePlayInputSettings {
bKeyboardEnabled: true;
bMouseEnabled: true;
bControllerEnabled: true;
}
export interface RemotePlaySettings {
bAV1DecodeAvailable: boolean;
bHEVCDecodeAvailable: boolean;
bRemotePlayDisabledBySystemPolicy: boolean;
bRemotePlaySupported: boolean;
bRemotePlayEnabled: boolean;
eRemotePlayP2PScope: EStreamP2PScope;
bRemotePlayServerConfigAvailable: boolean;
bRemotePlayServerConfigEnabled: boolean;
bRemotePlayClientConfigEnabled: boolean;
unStreamingSessionID: number;
strStreamingClientName: string;
/**
* If deserialized, returns {@link StreamingClientConfig}.
*/
RemotePlayClientConfig: StreamingClientConfig;
/**
* If deserialized, returns {@link StreamingServerConfig}.
*/
RemotePlayServerConfig: ArrayBuffer;
nDefaultAudioChannels: number;
nAutomaticResolutionX: number;
nAutomaticResolutionY: number;
}
export interface StreamingClientConfig {
quality?: EStreamQualityPreference;
desired_resolution_x?: number;
desired_resolution_y?: number;
desired_framerate_numerator?: number;
desired_framerate_denominator?: number;
desired_bitrate_kbps?: number;
enable_hardware_decoding?: boolean;
enable_performance_overlay?: boolean;
enable_video_streaming?: boolean;
enable_audio_streaming?: boolean;
enable_input_streaming?: boolean;
audio_channels?: number;
enable_video_hevc?: boolean;
enable_performance_icons?: boolean;
enable_microphone_streaming?: boolean;
controller_overlay_hotkey?: string;
enable_touch_controller_OBSOLETE?: boolean;
p2p_scope?: EStreamP2PScope;
enable_audio_uncompressed?: boolean;
display_limit?: StreamVideoLimit;
quality_limit?: StreamVideoLimit;
runtime_limit?: StreamVideoLimit;
decoder_limit: StreamVideoLimit[];
}
export interface StreamingServerConfig {
change_desktop_resolution?: boolean;
dynamically_adjust_resolution_OBSOLETE?: boolean;
enable_capture_nvfbc?: boolean;
enable_hardware_encoding_nvidia_OBSOLETE?: boolean;
enable_hardware_encoding_amd_OBSOLETE?: boolean;
enable_hardware_encoding_intel_OBSOLETE?: boolean;
software_encoding_threads?: number;
enable_traffic_priority?: boolean;
host_play_audio?: EStreamHostPlayAudioPreference;
enable_hardware_encoding?: boolean;
}
export interface StreamVideoLimit {
codec?: EStreamVideoCodec;
mode?: StreamVideoMode;
bitrate_kbps?: number;
burst_bitrate_kbps?: number;
}
export interface StreamVideoMode {
width?: number;
height?: number;
refresh_rate?: number;
refresh_rate_numerator?: number;
refresh_rate_denominator?: number;
}
export enum ERemoteClientLaunch {
OK = 1,
Fail,
RequiresUI,
RequiresLaunchOption,
RequiresEULA,
Timeout,
StreamTimeout,
StreamClientFail,
OtherGameRunning,
DownloadStarted,
DownloadNoSpace,
DownloadFiltered,
DownloadRequiresUI,
AccessDenied,
NetworkError,
Progress,
ParentalUnlockFailed,
ScreenLocked,
Unsupported,
DisabledLocal,
DisabledRemote,
Broadcasting,
Busy,
DriversNotInstalled,
TransportUnavailable,
Canceled,
Invisible,
RestrictedCountry,
Unauthorized,
}
export enum EStreamVideoCodec {
None,
Raw,
VP8,
VP9,
H264,
HEVC,
ORBX1,
ORBX2,
AV1,
}
export enum EStreamHostPlayAudioPreference {
Default,
Always,
}
export enum EStreamQualityPreference {
Automatic = -1,
Fast = 1,
Balanced,
Beautiful,
}
export enum EStreamP2PScope {
Automatic,
Disabled,
OnlyMe,
Friends,
Everyone,
}

View File

@@ -0,0 +1,149 @@
/**
* Interface for managing screenshots.
*/
export interface Screenshots {
/**
* Deletes a local screenshot.
* @param appId The ID of the application.
* @param screenshotIndex The index of the local screenshot.
* @returns a boolean value indicating whether the deletion was successful.
*/
DeleteLocalScreenshot(appId: string, screenshotIndex: number): Promise<boolean>;
DeleteLocalScreenshots(screenshots: ScreenshotToDelete[]): Promise<ScreenshotDeletionResponse>;
/**
* Retrieves all local screenshots for all applications.
* @returns an array of Screenshot objects.
*/
GetAllAppsLocalScreenshots(): Promise<Screenshot[]>;
/**
* Retrieves the count of all local screenshots for all applications.
* @returns the count of local screenshots.
*/
GetAllAppsLocalScreenshotsCount(): Promise<number>;
/**
* Retrieves a range of local screenshots for all applications.
* @param start The starting index of the screenshot range.
* @param end The ending index of the screenshot range.
* @returns an array of Screenshot objects within the specified range.
*/
GetAllAppsLocalScreenshotsRange(start: number, end: number): Promise<Screenshot[]>;
/**
* Retrieves all local screenshots.
* @returns an array of Screenshot objects.
*/
GetAllLocalScreenshots(): Promise<Screenshot[]>;
/**
* Retrieves the game associated with a specific local screenshot index.
* @param screenshotIndex The index of the local screenshot.
* @returns the ID of the game associated with the screenshot.
*/
GetGameWithLocalScreenshots(screenshotIndex: number): Promise<number>;
/**
* Retrieves the last taken local screenshot.
* @returns the last taken local screenshot.
*/
GetLastScreenshotTaken(): Promise<Screenshot>;
/**
* Retrieves a specific local screenshot for an application.
* @param appId The ID of the application.
* @param screenshotIndex The index of the local screenshot.
* @returns the requested local screenshot.
*/
GetLocalScreenshotByHandle(appId: string, screenshotIndex: number): Promise<Screenshot>;
/**
* Retrieves the count of local screenshots for a specific application.
* @param appId The ID of the application.
* @returns the count of local screenshots for the application.
*/
GetLocalScreenshotCount(appId: number): Promise<number>;
/**
* Retrieves the path of a screenshot.
* @param appId The ID of the application.
* @param hHandle The handle of the screenshot.
* @returns the screenshot path or the screenshot directory if no such handle.
*/
GetLocalScreenshotPath(appId: number, hHandle: number): Promise<string>;
/**
* Retrieves the number of games with local screenshots.
* @returns the number of games with local screenshots.
*/
GetNumGamesWithLocalScreenshots(): Promise<number>;
/**
* Gets total screenshot usage in the specified library folder.
* @param path Library folder path.
* @returns the number of taken space in bytes.
*/
GetTotalDiskSpaceUsage(path: string): Promise<number>;
/**
* Opens a local screenshot in the system image viewer.
* If the screenshot index is invalid, this function opens the screenshots directory for the specified application ID.
* @param appId The ID of the application.
* @param screenshotIndex The index of the local screenshot.
*/
ShowScreenshotInSystemViewer(appId: string, screenshotIndex: number): void;
/**
* Opens the folder containing local screenshots for a specific application.
* @param appId The ID of the application.
*/
ShowScreenshotsOnDisk(appId: string): void;
/**
* Uploads a local screenshot.
* @param appId The ID of the application.
* @param localScreenshot_hHandle The handle of the local screenshot.
* @param filePrivacyState The privacy state of the screenshot file.
* @returns a boolean value indicating whether the upload was successful.
*/
UploadLocalScreenshot(
appId: string,
localScreenshot_hHandle: number,
filePrivacyState: EUCMFilePrivacyState,
): Promise<boolean>;
}
export interface Screenshot {
nAppID: number;
strGameID: string;
hHandle: number;
nWidth: number;
nHeight: number;
nCreated: number; // timestamp
ePrivacy: EUCMFilePrivacyState;
strCaption: string;
bSpoilers: boolean;
strUrl: string;
bUploaded: boolean;
ugcHandle: string;
}
export interface ScreenshotToDelete {
gameID: string;
rgHandles: number[];
}
export interface ScreenshotDeletionResponse {
bSuccess: boolean;
rgFailedRequestIndices: number[];
}
export enum EUCMFilePrivacyState {
Invalid = -1,
Private = 1 << 1,
FriendsOnly = 1 << 2,
Public = 1 << 3,
Unlisted = 1 << 4,
}

View File

@@ -0,0 +1,372 @@
import { OperationResponse, Unregisterable } from "./shared";
/**
* Represents functionality for the server browser.
*/
export interface ServerBrowser {
/**
* Adds a favorite server.
* @param server The server to add.
* @returns an empty string if successful, `Invalid/missing IPv4?` if failed.
*/
AddFavoriteServer(server: GameServer): Promise<string>;
/**
* Adds a favorite server by IP.
* @param ip The IP to add to favorite servers.
* @returns an empty string if successful, localization string if failed.
*/
AddFavoriteServersByIP(ip: string): Promise<string>;
CancelServerQuery(dialogId: number, queryServer: number): void;
/**
* Connects to a server from a given dialog.
* @param dialogId The dialog ID to use.
* @param password Server password, empty if none.
* @returns a connection status.
*/
ConnectToServer(dialogId: number, password: string): Promise<EJoinServerError>;
/**
* Creates a server info dialog for the server your friend is currently playing on.
* @param pid 0
* @param steamId A Steam64 ID of a friend.
*/
CreateFriendGameInfoDialog(pid: number, steamId: string): void;
/**
* Creates a server info dialog.
* @param ip The server IP.
* @param port The server port.
* @param queryPort
* @returns the created dialog ID.
*/
CreateServerGameInfoDialog(ip: string, port: number, queryPort: number): Promise<number>;
/**
* Retrieves the server list.
* @param appId The game ID, 0 for every game.
* @param queryType The tab to use.
* @param filters Server filters.
* @param serverCallback What to do with the found server?
* @param requestCompletedCallback The callback function to be called when the request is completed.
* @returns the current server list request ID.
* @throws Throws if the query type is unknown.
* @throws Throws if the filter list isn't key/value pairs, i.e. of an even length.
* @remarks Stops at 10000 if there are more servers to be found.
* @example
* Filter examples, may be combined:
* ```
* [ 'gamedir', 'cstrike' ] // Doesn't work?
* [ 'hasplayers', '1' ] // Only works with "1"?
* [ 'notfull', '1' ] // Doesn't work?
* [ 'map', 'cs_office' ] // Has to be an exact match!
* ```
*/
/*
The enum in question:
(t =
'lan' == this.id
? this.all_servers.length > 0
? '#ServerBrowser_NoServersMatch'
: '#ServerBrowser_NoLanServers'
: 'internet' == this.id
? this.all_servers.length > 0
? '#ServerBrowser_NoInternetGamesMatch'
: e == l.zS.k_EServerFailedToRespond
? '#ServerBrowser_MasterServerNotResponsive'
: e == l.zS.k_ENoServersListedOnMasterServer
? '#ServerBrowser_MasterServerHasNoServersListed'
: '#ServerBrowser_NoInternetGamesResponded'
: 'favorites' == this.id
? this.all_servers.length > 0
? '#ServerBrowser_NoServersMatch'
: '#ServerBrowser_NoFavoriteServers'
: 'history' == this.id
? this.all_servers.length > 0
? '#ServerBrowser_NoHistoryServersMatch'
: '#ServerBrowser_NoServersPlayed'
: 'friends' == this.id
? this.all_servers.length > 0
? '#ServerBrowser_NoServersMatch'
: '#ServerBrowser_NoFriendsServers'
: 'BUGBUG'),
*/
CreateServerListRequest(
appId: number,
queryType: ServerBrowserTab_t,
filters: string[],
serverCallback: (server: GameServer) => void,
requestCompletedCallback: (response: number) => void,
): Promise<number | OperationResponse>;
/**
* Destroys the game info dialog functions (but not the window).
* @param dialogId The dialog ID to use.
* @remarks ServerBrowser.CancelServerQuery may throw if it tries to ping the server.
*/
DestroyGameInfoDialog(dialogId: number): void;
/**
* Stops retrieving the server list.
* @param activeServerListRequestId The active server request ID to use.
*/
DestroyServerListRequest(activeServerListRequestId: number): void;
/**
* Gets a list of games that support the server browser feature.
* @returns a list of games.
*/
GetMultiplayerGames(): Promise<ServerBrowserGame[]>;
/**
* Gets the server browser preferences.
* @returns server browser preferences.
*/
GetServerListPreferences(): Promise<ServerBrowserPreferences>;
/**
* Pings the server of a specified dialog ID.
* @param dialogId The dialog ID to use.
*/
PingServer(dialogId: number): Promise<number | OperationResponse>;
/**
* Registers a callback function to be called when a server gets added to favorite servers.
* @param callback The callback function to be called.
* @returns an object that can be used to unregister the callback.
*/
RegisterForFavorites(callback: (list: ServerBrowserFavoritesAndHistory) => void): Unregisterable;
/**
* Registers a callback function to be called when idk
* @param dialogId The dialog ID to use.
* @param callback The callback function to be called.
* @returns an object that can be used to unregister the callback.
*/
RegisterForFriendGamePlayed(
dialogId: number,
callback: (server: ServerBrowserFriendServer) => void,
): Unregisterable;
/**
* Registers a callback function to be called when a server info dialog opens.
* @param callback The callback function to be called.
* @returns an object that can be used to unregister the callback.
*/
RegisterForGameInfoDialogs(callback: (dialogs: ServerBrowserDialog[]) => void): Unregisterable;
/**
* Registers a callback function to be called when player details get requested.
* @param dialogId The dialog ID to use.
* @param callback The callback function to be called.
* @returns an object that can be used to unregister the callback.
*/
RegisterForPlayerDetails(
dialogId: number,
callback: (player: PlayerDetails) => void,
): Unregisterable;
/**
* Registers a callback function to be called when a server gets pinged.
* @param dialogId The dialog ID to use.
* @param callback The callback function to be called.
* @returns an object that can be used to unregister the callback.
*/
RegisterForServerInfo(dialogId: number, callback: (server: GameServer) => void): Unregisterable;
/**
* Removes a server from favorite servers.
* @param server The server to remove.
*/
RemoveFavoriteServer(server: GameServer): void;
/**
* Removes a server from history of played servers.
* @param server The server to remove.
*/
RemoveHistoryServer(server: GameServer): void;
/**
* Requests player details for a specific dialog.
* @param dialogId The dialog ID to use.
*/
RequestPlayerDetails(dialogId: number): Promise<number | OperationResponse>;
/**
* Sets the server browser preferences.
* @param prefs Server list preferences.
*/
SetServerListPreferences(prefs: ServerBrowserPreferences): void;
}
export type ServerBrowserTab_t = 'internet' | 'favorites' | 'history' | 'lan' | 'friends';
export interface ServerBrowserGame {
/** The ID of the game. */
appid: number;
/** The ID of the game. */
gameid: string;
/** The game folder. */
gamedir: string;
/** The game's name. */
name: string;
}
export interface ServerBrowserPreferences {
GameList: string;
filters: ServerBrowserTabFilters;
}
export type ServerBrowserTabFilters = {
[tab in ServerBrowserTab_t]: ServerBrowserGameFilter;
};
export interface ServerBrowserGameFilter {
/** Has users playing */
NoEmpty: boolean;
/** Server not full */
NoFull: boolean;
/** Is not password protected */
NoPassword: boolean;
/** Anti-cheat */
Secure: EServerBrowserGameFilterAntiCheat;
/** The ID of the game */
appid: number;
/** The game folder */
game: string;
/** Map filter */
map: string;
/** Latency */
ping: EServerBrowserGameFilterPing;
}
export enum EServerBrowserGameFilterAntiCheat {
All,
Secure,
NotSecure,
}
export enum EServerBrowserGameFilterPing {
All,
LessThan50 = 50,
LessThan100 = 100,
LessThan150 = 150,
LessThan250 = 250,
}
export interface ServerBrowserFavoritesAndHistory {
favorites: GameServer[];
history: GameServer[];
}
export interface ServerBrowserFriendServer {
/** The ID of the game. */
appid: number;
/** Non-Steam server? */
bNonSteamServer: boolean;
gameText: string;
/** The ID of the game. */
gameid: string;
steamIDLobby: string;
}
export interface ServerBrowserDialog {
dialogID: number;
ip: number;
port: number;
queryPort: number;
}
export interface GameServer {
/** The ID of the game. */
appid: number;
/** Do not refresh if had unsuccessful response? */
bDoNotRefresh?: boolean;
/** Found the server? */
bHadSuccessfulResponse: boolean;
/** Has password? */
bPassword: boolean;
/** Is VAC secured? */
bSecure: boolean;
/** How many bot players there currently are. */
botPlayers: number;
/** The server's game name/description. */
gameDesc: string;
/** The game folder. */
gameDir: string;
/** Server tags, separated by a comma. */
gameTags: string;
/** The server IP. */
ip: string;
/** Last time played as a UNIX timestamp. */
lastPlayed: number;
/** Current server map. */
map: string;
/** Max players on the server. */
maxPlayers: number;
/** The server name. */
name: string;
/** The latency to the server. */
ping: number;
/** How many players there currently are. */
players: number;
/** The server port. */
port: number;
queryPort: number;
/** The server's game version it is running on. */
serverVersion: number;
/** Game server account ID. */
steamID: string;
}
export enum EJoinServerError {
PingFailed = -3,
Connecting,
Pinging,
None,
VACBanned,
ServerFull,
ModNotInstalled,
AppNotFound,
NotInitialized,
}
export interface PlayerDetails {
/**
* `true` is the server refresh is successful.
*/
bSuccess: boolean;
/**
* `true` when the server refresh is done.
*/
bRefreshComplete: boolean;
/**
* Player name.
*
* @note Defined when {@link bRefreshComplete} is `true`.
*/
playerName?: string;
/**
* Player score.
*
* @note Defined when {@link bRefreshComplete} is `true`.
*/
score?: number;
/**
* Time played on the server in seconds
*
* @note Defined when {@link bRefreshComplete} is `true`.
*/
timePlayed?: number;
}

View File

@@ -0,0 +1,663 @@
import {CompatibilityTool as CompatibilityTool} from "./App";
import { JsPbMessage, OperationResponse, Unregisterable } from "./shared";
export interface Settings {
AddClientBeta(name: string, password: string): void;
/**
* Clears HTTP cache located in `<STEAMPATH>/appcache/httpcache`.
*/
ClearAllHTTPCaches(): void;
/**
* Clears download cache and logs you out.
*/
ClearDownloadCache(): void;
GetAccountSettings(): Promise<AccountSettings>;
GetAppUsesP2PVoice(appId: number): Promise<boolean>;
GetAvailableLanguages(): Promise<Language[]>;
GetAvailableTimeZones(): Promise<TimeZone[]>;
// Returns the current language "english"
GetCurrentLanguage(): Promise<string>;
GetGlobalCompatTools(): Promise<CompatibilityTool[]>;
/**
* @returns a ProtoBuf message. If deserialized, returns {@link MsgMonitorInfo}.
*/
GetMonitorInfo(): Promise<ArrayBuffer>;
GetOOBETestMode(): Promise<boolean>;
GetRegisteredSteamDeck(): Promise<RegisteredSteamDeck>;
// Returns the current timezone
GetTimeZone(): Promise<string>;
GetWindowed(): Promise<boolean>;
IgnoreSteamDeckRewards(): void;
/**
* Opens the Windows microphones dialog.
*/
OpenWindowsMicSettings(): void;
RegisterForMicVolumeUpdates: Unregisterable;
/**
* If `data` is deserialized, returns {@link MsgClientSettings}.
* @returns an object that can be used to unregister the callback.
*/
RegisterForSettingsArrayChanges(callback: (data: ArrayBuffer) => void): Unregisterable;
RegisterForSettingsChanges(callback: (settings: SteamSettings) => void): Unregisterable;
RegisterForTimeZoneChange(callback: (timezoneId: string) => void): Unregisterable; // When timezone is changed from settings, callback will return new timezoneId
ReinitMicSettings(): void;
RenderHotkey(event: KeyCaptureEvent): Promise<string>;
RequestDeviceAuthInfo(): void;
SelectClientBeta(nBetaID: number): void;
// Get from available languages
SetCurrentLanguage(strShortName: string): void;
SetEnableSoftProcessKill(value: boolean): void; // Default value is false, this is Valve internal menu
SetHostname(hostname: string): void;
SetMicTestMode(value: boolean): void;
SetOOBETestMode(value: boolean): void;
SetPreferredMonitor(monitor: string): void;
SetRegisteredSteamDeck(steam64Id: string, serialNumber: string): void;
/**
* Sets the "Don't save account credentials on this computer" option.
* @param value Whether to save account credentials.
*/
SetSaveAccountCredentials(value: boolean): void;
/**
* @param base64 Serialized base64 message from `CMsgClientSettings`.
* @returns a boolean indicating whether the operation was successful.
*/
SetSetting(base64: string): Promise<boolean>;
/**
* You can get valid timezoneIds from {@link GetAvailableTimeZones}.
*/
SetTimeZone(timezoneId: string): void;
SetUseNintendoButtonLayout(controllerIndex: number, value: boolean): void;
SetUseUniversalFaceButtonGlyphs(nControllerIndex: number, value: boolean): void;
SetWindowed(value: boolean): void;
SpecifyGlobalCompatTool(strToolName: string): void;
ToggleSteamInstall(): Promise<OperationResponse>;
}
export interface AccountSettings {
strEmail: string;
bEmailValidated: boolean;
bHasAnyVACBans: boolean;
bHasTwoFactor: boolean;
eSteamGuardState: ESteamGuardState;
rtSteamGuardEnableTime: number;
bSaveAccountCredentials: boolean;
}
/**
* @todo unconfirmed, taken from localization strings
*/
export enum ESteamGuardState {
EmailUnverified,
Protected,
Disabled,
Offline,
NotEnabled,
}
export interface KeyCaptureEvent {
alt_key: boolean;
ctrl_key: boolean;
display_name: string;
meta_key: boolean;
shift_key: boolean;
}
export interface Language {
language: ELanguage;
strShortName: string;
}
export enum ELanguage {
None = -1,
English,
German,
French,
Italian,
Korean,
Spanish,
SimplifiedChinese,
TraditionalChinese,
Russian,
Thai,
Japanese,
Portuguese,
Polish,
Danish,
Dutch,
Finnish,
Norwegian,
Swedish,
Hungarian,
Czech,
Romanian,
Turkish,
Brazilian,
Bulgarian,
Greek,
Arabic,
Ukrainian,
LatamSpanish,
Vietnamese,
SteamChina_SChinese,
Max,
}
export interface RegisteredSteamDeck {
bRegistered: boolean;
bIgnoreRegistrationPrompt: boolean;
strSteamID: string;
strSerialNumber: string;
}
export interface TimeZone {
utcOffset: number;
timezoneID: string;
timezoneLocalizationToken: string;
regionsLocalizationToken: string;
}
interface Region {
nRegionID: number;
strRegionName: string;
}
interface Hour {
nHour: number;
strDisplay: string;
}
interface AvailableClientBeta {
nBetaID: number;
strName: string;
}
interface SteamSettings {
bIsInClientBeta: boolean;
bIsSteamSideload: boolean;
eClientBetaState: EClientBetaState;
strSelectedBetaName: string;
nAvailableBetas: number;
bChangeBetaEnabled: boolean;
nSelectedBetaID: number;
vecAvailableClientBetas: AvailableClientBeta[];
bIsValveEmail: boolean;
bIsInDesktopUIBeta: boolean;
bEnableSoftProcessKill: boolean;
vecValidDownloadRegions: Region[];
vecValidAutoUpdateRestrictHours: Hour[];
bCompatEnabled: boolean;
bCompatEnabledForOtherTitles: boolean;
strCompatTool: string;
strDisplayName: string;
bDisplayIsExternal: boolean;
flAutoDisplayScaleFactor: number;
flCurrentDisplayScaleFactor: number;
bDisplayIsUsingAutoScale: boolean;
flMinDisplayScaleFactor: number;
flMaxDisplayScaleFactor: number;
flCurrentUnderscanLevel: number;
bUnderscanEnabled: boolean;
vecNightModeScheduledHours: Hour[];
}
export enum EClientBetaState {
None,
NoneChosen,
NoneChosenNonAdmin,
InBeta,
InBetaNonAdmin,
}
/**
* CMsgMonitorInfo
*/
export interface MsgMonitorInfo extends JsPbMessage {
monitors(): Monitor[];
selected_display_name(): string;
add_monitors(param0: any, param1: any): any;
set_monitors(param0: any): any;
set_selected_display_name(param0: any): any;
}
/**
* @todo Doesn't work on Linux ?
*/
export interface Monitor {
monitor_device_name: string;
monitor_display_name: string;
}
/**
* CMsgClientSettings
*/
export interface MsgClientSettings extends JsPbMessage {
always_show_user_chooser(): boolean;
always_use_gamepadui_overlay(): boolean;
auto_scale_factor(): number;
bigpicture_windowed(): boolean;
broadcast_bitrate(): number;
broadcast_chat_corner(): number;
broadcast_encoding_option(): EBroadcastEncoderSetting;
broadcast_output_height(): number;
broadcast_output_width(): number;
broadcast_permissions(): EBroadcastPermission;
broadcast_record_all_audio(): boolean;
broadcast_record_all_video(): boolean;
broadcast_record_microphone(): boolean;
broadcast_show_live_reminder(): boolean;
broadcast_show_upload_stats(): boolean;
cef_remote_debugging_enabled(): boolean;
cloud_enabled(): boolean;
controller_combine_nintendo_joycons(): boolean;
controller_enable_chord(): boolean;
controller_generic_support(): boolean;
controller_guide_button_focus_steam(): boolean;
controller_poll_rate(): boolean;
controller_power_off_timeout(): number;
controller_ps_support(): number;
controller_switch_support(): boolean;
controller_xbox_driver(): boolean;
controller_xbox_support(): boolean;
default_ping_rate(): number;
disable_all_toasts(): boolean;
disable_toasts_in_game(): boolean;
display_name(): string;
download_peer_content(): number;
download_rate_bits_per_s(): boolean;
download_region(): number;
download_throttle_rate(): number;
download_throttle_while_streaming(): boolean;
download_while_app_running(): boolean;
enable_avif_screenshots(): boolean;
enable_dpi_scaling(): boolean;
enable_gpu_accelerated_webviews(): boolean;
enable_hardware_video_decoding(): boolean;
enable_marketing_messages(): boolean;
enable_overlay(): boolean;
enable_screenshot_notification(): boolean;
enable_screenshot_sound(): boolean;
enable_shader_background_processing(): boolean;
enable_shader_precache(): boolean;
enable_ui_sounds(): boolean;
force_deck_perf_tab(): boolean;
force_fake_mandatory_update(): boolean;
force_oobe(): boolean;
g_background_audio(): EGRAudio;
g_background_a_m(): number;
g_background_a_s(): boolean;
g_background_path(): string;
g_background_max_keep(): string;
g_background_mode(): EGRMode;
g_background_time_resolution(): number;
g_background_mk(): CMsgHotkey;
g_background_tg(): CMsgHotkey;
g_max_fps(): number;
gamerecording_automatic_gain_control(): boolean;
gamerecording_export_codec(): EExportCodec;
gamerecording_export_directory(): number;
gamerecording_export_limit_bitrate(): number;
gamerecording_export_limit_frame_rate(): number;
gamerecording_export_limit_height(): number;
gamerecording_export_limit_size_mb(): number;
gamerecording_export_limit_width(): number;
gamerecording_export_limit_type(): EGRExportLimitType;
gamerecording_force_mic_mono(): boolean;
gamerecording_hotkey_ic(): CMsgHotkey;
gamerecording_ic_seconds(): number;
gamerecording_video_bitrate(): string;
gamerecording_video_maxheight(): number;
game_notes_enable_spellcheck(): boolean;
gamescope_allow_tearing(): boolean;
gamescope_app_target_framerate(): number;
gamescope_composite_debug(): boolean;
gamescope_disable_framelimit(): boolean;
gamescope_disable_mura_correction(): boolean;
gamescope_display_refresh_rate(): number;
gamescope_enable_app_target_framerate(): boolean;
gamescope_force_composite(): boolean;
gamescope_hdr_visualization(): EHDRVisualization;
gamescope_include_steamui_in_screenshots(): boolean;
gamescope_use_game_refresh_rate_in_steam(): boolean;
gamestream_enable_video_h265(): boolean;
gamestream_hardware_video_encode(): boolean;
hdr_compat_testing(): boolean;
in_client_beta(): boolean;
is_external_display(): boolean;
is_steam_sideloaded(): boolean;
jumplist_flags(): number;
library_disable_community_content(): boolean;
library_display_icon_in_game_list(): boolean;
library_display_size(): number;
library_low_bandwidth_mode(): boolean;
library_low_perf_mode(): boolean;
library_whats_new_show_only_product_updates(): boolean;
max_scale_factor(): number;
min_scale_factor(): number;
music_download_high_quality(): boolean;
music_pause_on_app_start(): boolean;
music_pause_on_voice_chat(): boolean;
music_playlist_notification(): boolean;
music_volume(): number;
needs_steam_service_repair(): boolean;
no_save_personal_info(): boolean;
oobe_test_mode_enabled(): boolean;
os_version_unsupported(): boolean;
overlay_fps_counter_corner(): number;
overlay_fps_counter_high_contrast(): boolean;
overlay_key(): CMsgHotkey;
overlay_restore_browser_tabs(): boolean;
overlay_scale_interface(): boolean;
overlay_tabs(): string;
overlay_toolbar_list_view(): boolean;
override_browser_composer_mode(): number;
play_sound_on_toast(): boolean;
preferred_monitor(): string;
ready_to_play_includes_streaming(): boolean;
restrict_auto_updates(): boolean;
restrict_auto_updates_end(): number;
restrict_auto_updates_start(): number;
run_at_startup(): boolean;
save_uncompressed_screenshots(): boolean;
screenshot_items_per_row(): number;
screenshot_key(): CMsgHotkey;
screenshots_path(): string;
server_ping_rate(): number;
setting_validation_bool(): boolean;
setting_validation_enum(): EHDRVisualization;
setting_validation_int32(): number;
setting_validation_uint32(): number;
setting_validation_uint64(): number;
setting_validation_float(): number;
setting_validation_string(): string;
shader_precached_size(): string;
show_copy_count_in_library(): boolean;
show_family_sharing_notifications(): boolean;
show_screenshot_manager(): boolean;
show_steam_deck_info(): boolean;
show_store_content_on_home(): boolean;
show_timestamps_in_console(): boolean;
skip_steamvr_install_dialog(): boolean;
small_mode(): boolean;
smooth_scroll_webviews(): boolean;
start_in_big_picture_mode(): boolean;
start_page(): string;
startup_movie_id(): string;
startup_movie_local_path(): string;
startup_movie_shuffle(): boolean;
startup_movie_used_for_resume(): boolean;
steam_cef_gpu_blocklist_disabled(): boolean;
steam_input_configurator_error_msg_enable(): boolean;
steam_networking_share_ip(): number;
steam_os_underscan_enabled(): boolean;
steam_os_underscan_level(): number;
steamos_cec_enabled(): boolean;
steamos_cec_wake_on_resume(): boolean;
steamos_magnifier_scale(): number;
steamos_status_led_brightness(): number;
steamos_tdp_limit(): number;
steamos_tdp_limit_enabled(): boolean;
steamos_wifi_debug(): boolean;
steamos_wifi_force_wpa_supplicant(): boolean;
system_bluetooth_enabled(): boolean;
turn_off_controller_on_exit(): boolean;
voice_mic_device_name(): string;
voice_mic_input_gain(): number;
voice_push_to_talk_key(): CMsgHotkey;
voice_push_to_talk_setting(): number;
voice_speaker_output_gain(): number;
web_browser_home(): string;
}
export interface CMsgHotkey extends JsPbMessage {
key_code(): number;
alt_key(): boolean;
shift_key(): boolean;
ctrl_key(): boolean;
meta_key(): boolean;
display_name(): string;
}
export enum EBroadcastEncoderSetting {
BestQuality,
BestPerformance,
}
export enum EBroadcastPermission {
Disabled,
FriendsApprove,
FriendsAllowed,
Public,
Subscribers,
}
export enum EExportCodec {
Default,
H264,
H265,
}
export enum EGRAudio {
Game,
System,
Select,
}
export enum EGRExportLimitType {
Native,
FileSize,
Advanced,
}
export enum EGRMode {
Never,
Always,
Manual,
}
export enum EHDRVisualization {
None,
Heatmap,
Analysis,
HeatmapExtended,
HeatmapClassic,
}

View File

@@ -0,0 +1,43 @@
import { ESteamRealm, Unregisterable } from "./shared";
/**
* `hSharedConnection` is the number from {@link AllocateSharedConnection}.
*/
export interface SharedConnection {
AllocateSharedConnection(): Promise<number>;
// if no such number, sends this warning:
// src\clientdll\clientsharedconnection.cpp (154) : m_mapSharedConnections.HasElement( hSharedConnection )
Close(hSharedConnection: number): void;
RegisterOnBinaryMessageReceived(hSharedConnection: number, callback: (data: ArrayBuffer) => void): Unregisterable;
RegisterOnLogonInfoChanged(hSharedConnection: number, callback: (info: LogonInfo) => void): Unregisterable;
RegisterOnMessageReceived(hSharedConnection: number, callback: (param0: any) => void): Unregisterable;
SendMsg: any;
SendMsgAndAwaitBinaryResponse: any;
SendMsgAndAwaitResponse(hSharedConnection: number, msg: string): Promise<any>;
SubscribeToClientServiceMethod(hSharedConnection: number, param1: any): any;
SubscribeToEMsg(hSharedConnection: number, param1: any): any;
}
export interface LogonInfo {
/** `true` if logged on. */
bLoggedOn: boolean;
eUniverse: ESteamRealm;
/** Account username. Empty if not logged on. */
strAccountName: string;
/** URL for community content. */
strCommunityImagesURL: string;
/** Account nickname. Empty if not logged on. */
strPersonaName: string;
/** Steam64 ID. */
strSteamid: string;
/** Country code. */
strUserCountry: string;
}

View File

@@ -0,0 +1,10 @@
export interface Stats {
// param0 - AppDetailsReviewSection, Showcases, LibraryReviewSpotlight
// param1 -
// AppDetailsReviewSection: PositiveClicked, NegativeClicked, NeutralClicked, PositiveReviewPosted, NegativeReviewPosted, EditClicked, ReviewCanceled
// LibraryReviewSpotlight: ReviseClicked, PositiveClicked, ReviseCloseClicked, NegativeClicked, PositiveRevisePosted, NegativeRevisePosted, ReviseCanceled, ReviewCanceled, CloseClicked
// Showcases: Delete, Save-Modify, Save-New
RecordActivationEvent(param0: string, param1: string): void;
RecordDisplayEvent(param0: boolean, param1: string, param2: string): void;
}

View File

@@ -0,0 +1,3 @@
export interface SteamChina {
GetCustomLauncherAppID(): Promise<number>;
}

View File

@@ -0,0 +1,24 @@
import { OperationResponse } from "./shared";
/**
* SteamClient.MachineStorage affects the "STEAMPATH/config/config.vdf" file.
* SteamClient.RoamingStorage affects the "STEAMPATH/userdata/STEAMID3/7/remote/sharedconfig.vdf" file.
* SteamClient.Storage affects the "STEAMPATH/userdata/STEAMID3/config/localconfig.vdf" file.
*/
export interface Storage {
DeleteKey(key: string): Promise<OperationResponse | undefined>;
/**
* @remarks Use {@link SetObject} to set.
*/
GetJSON(key: string): Promise<OperationResponse | string>;
GetString(key: string): Promise<OperationResponse | string>;
/**
* @remarks Use {@link SetObject} to get.
*/
SetObject(key: string, value: object): Promise<OperationResponse | undefined>;
SetString(key: string, value: string): Promise<OperationResponse | undefined>;
}

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