Compare commits

..

1 Commits

Author SHA1 Message Date
AAGaming
d6e399fa46 feat(plugin): add menu and overlay patching api 2022-12-31 21:48:31 -05:00
28 changed files with 1587 additions and 2659 deletions

View File

@@ -1,247 +1,3 @@
## [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)
### Bug Fixes
* add patch indicator to prevent crashes ([3170779](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/3170779c6b3d02ea17f7b6c1fbd57e00498ffe4f))
### Features
* add components found while working on tabmaster ([4f8f65d](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/4f8f65d42979149cc80b4a86545d3d0d9bf14bf3))
* add components found while working on tabmaster ([fd0d011](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/fd0d011cbf05790c5a1078970b5be72f9267402c))
## [3.21.8](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.21.7...v3.21.8) (2023-06-27)
### Bug Fixes
* the typescript compiler was eating the enum ([c9b5839](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/c9b583945c1cb5267b41a821743590a841572abe))
## [3.21.7](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.21.6...v3.21.7) (2023-06-26)
### Bug Fixes
* add patch indicator to prevent crashes ([#88](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/88)) ([9128c1e](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/9128c1e7dadb98a8926d3dba9907a01cc78d90cf))
## [3.21.6](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.21.5...v3.21.6) (2023-06-22)
### Bug Fixes
* reposition parameter for file picker V2 ([ee51dc5](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/ee51dc5fc0dd5bdc2b0b9e10aa27607fbe51f491))
## [3.21.5](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.21.4...v3.21.5) (2023-06-22)
### Bug Fixes
* missing parameter ([8b54ee9](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/8b54ee990ee4d9b51174737979c35ab7ad92ed7a))
## [3.21.4](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.21.3...v3.21.4) (2023-06-22)
### Bug Fixes
* move the new file picker api as v2 ([8bfeae4](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/8bfeae4b3593b2efa0aa075a0d9e0b5926cdf169))
## [3.21.3](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.21.2...v3.21.3) (2023-06-22)
### Bug Fixes
* total is not needed as a return value to plugins ([979a630](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/979a630f2b02ac4a1ac19e38002244f9dfe97177))
## [3.21.2](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.21.1...v3.21.2) (2023-06-18)
### Bug Fixes
* **SidebarNavigation:** allow ReactNode for page title ([44d9b90](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/44d9b90cdcfb9e6441fdb1e4b21fe844f1f29fd5))
## [3.21.1](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.21.0...v3.21.1) (2023-05-28)
### Bug Fixes
* **reorderable-list:** open label type from string to ReactNode ([fb5f043](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/fb5f043ba9eeed0209960c8ae4ae597c2831b8c8))
# [3.21.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.20.7...v3.21.0) (2023-05-19)
### Bug Fixes
* **Utils:** return null when SP not found ([18d341f](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/18d341f82acf843ff830d1bf3d44678f3f2eda52))
### Features
* **StaticClasses:** add new GamepadUI classes ([3ca8c43](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/3ca8c43a542b1049c4e203ad186e555401fbfbfe))
## [3.20.7](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.20.6...v3.20.7) (2023-05-10)
### Bug Fixes
* **decky:** fix decky on latest beta ([74a7cba](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/74a7cbaf94538c68a01f5fa707935c4d21570c5f))
## [3.20.6](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.20.5...v3.20.6) (2023-04-29)
### Bug Fixes
* **QuickAccessTab:** set decky tab ID to 999 ([5d5cb31](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/5d5cb31638070deae9970a93c587b447d5e56559))
## [3.20.5](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.20.4...v3.20.5) (2023-04-04)
### Bug Fixes
* reorderable list no longer toggles on backout ([7e1182a](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/7e1182a83f7fafbec6fe115a72f8b64b71c119a4))
## [3.20.4](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.20.3...v3.20.4) (2023-04-04)
### Bug Fixes
* **SteamSpinner:** oh apparently the class was moved outside the component for some reason?????? ([b8ddf3d](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/b8ddf3d927401d04c0fde8ebc8960639369b8ad3))
## [3.20.3](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.20.2...v3.20.3) (2023-04-04)
### Bug Fixes
* **SteamSpinner:** fix the fix ([79d229b](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/79d229be50d26e2510af0ea16cdf6644371a5967))
## [3.20.2](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.20.1...v3.20.2) (2023-04-04)
### Bug Fixes
* **SteamSpinner:** dont error on latest desktop beta ([19819b7](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/19819b7a5bc9434fa802f4e8dca4f4cb6921df07))
## [3.20.1](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.20.0...v3.20.1) (2023-04-03)
### Bug Fixes
* **useQuickAccessVisible:** make it work again ([7dacb23](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/7dacb23e8be7b1f076cdd0869a4e3a3902b07ec5))
# [3.20.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.19.2...v3.20.0) (2023-04-03)
### Bug Fixes
* **Field:** remove style ([53faf55](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/53faf55df1484204e276cd21a32703c2d7809332))
* fixed missing export ([b480d39](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/b480d397c4251f42c1f24ff5e74322d22e313f05))
* list didn't update on prop change ([b146eab](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/b146eab8d7e1338afa54a168a2fd895e716c2bb2))
### Features
* added doc comments ([dfcb3be](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/dfcb3bec19900e099ae3766771e120dbd4f229f5))
* added reorderable list and updated fieldProps ([3c171cf](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/3c171cfb8ff18ed02eeb569a183c9d43fd0b4f57))
* changed ReorderableList to working version ([5b166d6](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/5b166d6db879200b049e872cba327957ba5fb705))
* made requested changes and ran prettier ([b1b2f4f](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/b1b2f4fa2da755efd65b82b15b52196f89fb09c0))
* refactoring mostly complete ([cea315a](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/cea315a52c285b31ad4e5d0a03104c674ae4f7f5))
* reorderable list now saves on backout ([b1591f8](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/b1591f86bbd36e160818626760e0717ee50878e0))
* **ReorderableList:** add animations, clean up ([26fae13](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/26fae13c8ebd3f11f134c3bc0edfc971afd42fff))
* support for non-interactable reordering ([dbd01b1](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/dbd01b11cafe9b102cc371b9812f99aec718d106))
* support for user specified icon ([47a6fdd](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/47a6fddc89b8f9110252c5e19a6e95152c367dbf))
## [3.19.2](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.19.1...v3.19.2) (2023-03-07)
### Bug Fixes
* **Item:** add highlightOnFocus prop ([c53f87b](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/c53f87b4a9273b377853bfff1d27474ebd6e564a))
## [3.19.1](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.19.0...v3.19.1) (2023-02-23)
### Bug Fixes
* refactoring to fix for feb 22 2023 beta ([0b6dc24](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/0b6dc24c0da2d7644e185425e975787657f8bba1))
# [3.19.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.18.11...v3.19.0) (2023-02-22)
### Features
* added reorderable list and updated fieldProps ([#57](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/57)) ([5a074b5](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/5a074b5bb68c675c484a7b693f67a67488be9bcf))
## [3.18.11](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.18.10...v3.18.11) (2023-02-18)
### Bug Fixes
* **Navigation:** fix NavigateToExternalWeb ([82214fe](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/82214fef4c0a383776631fbb754550fe69f9000d))
* **Navigation:** fix NavigateToExternalWeb ([c53d7f8](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/c53d7f8448d8aad76dc699f1f309bdd547ee14df))
## [3.18.10](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.18.9...v3.18.10) (2023-01-17)
### Bug Fixes
* **SuspensefulImage:** fix changing src ([9723854](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/9723854ddca53d7708e1effbddec9e5ead22d5de))
## [3.18.9](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.18.8...v3.18.9) (2023-01-16)
### Bug Fixes
* **Navigation:** fix on stable ([4affd4a](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/4affd4aaec088f01d0f30af48cb4daa34acf26b1))
## [3.18.8](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.18.7...v3.18.8) (2023-01-16)
### Bug Fixes
* **Navigation:** fix timing issue in decky-loader ([58b69f0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/58b69f0d6c43356c4f0ed183802d5bf7fb80e978))
## [3.18.7](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.18.6...v3.18.7) (2023-01-16)
### Bug Fixes
* un-break navigation on stable ([2e66e5a](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/2e66e5a555f44009d24e332eca82453ba930baf7))
## [3.18.6](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.18.5...v3.18.6) (2023-01-13)
### Bug Fixes
* **Router:** fix Navigation for the millionth time ([aac2d52](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/aac2d520a68b1074ba1ae988d6c92f7881a296d7))
## [3.18.5](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.18.4...v3.18.5) (2022-12-21)
### Bug Fixes
* fixed prop interfaces ([#70](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/70)) ([0b50f2c](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/0b50f2cf0baa76fc00aa0a41a8435d7a512bff19))
## [3.18.4](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v3.18.3...v3.18.4) (2022-12-16)

View File

@@ -2,9 +2,9 @@
Library used to develop plugins used for use with [decky-loader](https://github.com/SteamDeckHomebrew/decky-loader).
## Decky Loader Discord [![Chat](https://img.shields.io/discord/960281551428522045?color=%235865F2&label=discord)](https://deckbrew.xyz/discord)
## Decky Loader Discord [![Chat](https://img.shields.io/badge/chat-on%20discord-7289da.svg)](https://discord.gg/ZU74G2NJzk)
Please [contact the developers here](https://deckbrew.xyz/discord) for questions and support that cannot be addressed via a Github issue.
Please contact the developers here for questions and support that cannot be addressed via a Github issue.
## Developers and Contributors

View File

@@ -1,6 +1,6 @@
{
"name": "decky-frontend-lib",
"version": "3.23.1",
"version": "3.18.4",
"description": "A library for building decky plugins",
"main": "dist/index.js",
"types": "dist/index.d.ts",
@@ -9,7 +9,7 @@
"scripts": {
"build": "shx rm -rf dist && tsc -b",
"dev": "tsc -b -w",
"docs": "typedoc --theme wiki-js --tsconfig ./tsconfig.json src/**/*",
"docs": "typedoc --tsconfig ./tsconfig.json src/**/*",
"test": "echo 'No tests for now!'",
"prepare": "husky install",
"commit": "git-cz"
@@ -59,11 +59,11 @@
"semantic-release": "^19.0.3",
"shx": "^0.3.4",
"ts-jest": "^27.1.4",
"typedoc": "^0.23.24",
"typedoc": "^0.23.15",
"typedoc-plugin-markdown": "^3.13.6",
"typedoc-plugin-mdn-links": "^2.0.0",
"typedoc-plugin-missing-exports": "^1.0.0",
"typedoc-wikijs-theme": "^1.0.5",
"typescript": "^4.9.5"
"typescript": "^4.6.3"
},
"pnpm": {
"peerDependencyRules": {

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

File diff suppressed because it is too large Load Diff

View File

@@ -1,183 +0,0 @@
import { Fragment, JSXElementConstructor, ReactElement, ReactNode, useEffect, useState } from 'react';
import { Field, FieldProps, Focusable, GamepadButton } from '../deck-components';
/**
* A ReorderableList entry of type <T>.
* @param label The name of this entry in the list.
* @param data Optional data to connect to this entry.
* @param position The position of this entry in the list.
*/
export type ReorderableEntry<T> = {
label: ReactNode;
data?: T;
position: number;
};
/**
* Properties for a ReorderableList component of type <T>.
*
* @param animate If the list should animate. @default true
*/
export type ReorderableListProps<T> = {
entries: ReorderableEntry<T>[];
onSave: (entries: ReorderableEntry<T>[]) => void;
interactables?: JSXElementConstructor<{ entry: ReorderableEntry<T> }>;
fieldProps?: FieldProps;
animate?: boolean;
};
/**
* A component for creating reorderable lists.
*
* See an example implementation {@linkplain https://github.com/Tormak9970/Component-Testing-Plugin/blob/main/src/testing-window/ReorderableListTest.tsx here}.
*/
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),
);
const [reorderEnabled, setReorderEnabled] = useState<boolean>(false);
useEffect(() => {
setEntryList(props.entries.sort((a: ReorderableEntry<T>, b: ReorderableEntry<T>) => a.position - b.position));
}, [props.entries]);
function toggleReorderEnabled(): void {
let newReorderValue = !reorderEnabled;
setReorderEnabled(newReorderValue);
if (!newReorderValue) {
props.onSave(entryList);
}
}
function saveOnBackout(e: Event) {
const event = e as CustomEvent;
if (event.detail.button == GamepadButton.CANCEL && reorderEnabled) {
setReorderEnabled(!reorderEnabled);
props.onSave(entryList);
}
}
return (
<Fragment>
<div
style={{
width: 'inherit',
height: 'inherit',
flex: '1 1 1px',
scrollPadding: '48px 0px',
display: 'flex',
flexDirection: 'column',
justifyContent: 'flex-start',
alignContent: 'stretch',
}}
>
<Focusable
onSecondaryButton={toggleReorderEnabled}
onSecondaryActionDescription={reorderEnabled ? 'Save Order' : 'Reorder'}
onClick={toggleReorderEnabled}
onButtonDown={saveOnBackout}
>
{entryList.map((entry: ReorderableEntry<T>) => (
<ReorderableItem
animate={props.animate!}
listData={entryList}
entryData={entry}
reorderEntryFunc={setEntryList}
reorderEnabled={reorderEnabled}
fieldProps={props.fieldProps}
>
{props.interactables ? <props.interactables entry={entry} /> : null}
</ReorderableItem>
))}
</Focusable>
</div>
</Fragment>
);
}
/**
* Properties for a ReorderableItem component of type <T>
*/
export type ReorderableListEntryProps<T> = {
fieldProps?: FieldProps;
listData: ReorderableEntry<T>[];
entryData: ReorderableEntry<T>;
reorderEntryFunc: CallableFunction;
reorderEnabled: boolean;
animate: boolean;
children: ReactElement | null;
};
function ReorderableItem<T>(props: ReorderableListEntryProps<T>) {
const [isSelected, _setIsSelected] = useState<boolean>(false);
const [isSelectedLastFrame, setIsSelectedLastFrame] = useState<boolean>(false);
const listEntries = props.listData;
function onReorder(e: Event): void {
if (!props.reorderEnabled) return;
const event = e as CustomEvent;
const currentIdx = listEntries.findIndex((entryData: ReorderableEntry<T>) => entryData === props.entryData);
const currentIdxValue = listEntries[currentIdx];
if (currentIdx < 0) return;
let targetPosition: number = -1;
if (event.detail.button == GamepadButton.DIR_DOWN) {
targetPosition = currentIdxValue.position + 1;
} else if (event.detail.button == GamepadButton.DIR_UP) {
targetPosition = currentIdxValue.position - 1;
}
if (targetPosition >= listEntries.length || targetPosition < 0) return;
let otherToUpdate = listEntries.find((entryData: ReorderableEntry<T>) => entryData.position === targetPosition);
if (!otherToUpdate) return;
let currentPosition = currentIdxValue.position;
currentIdxValue.position = otherToUpdate.position;
otherToUpdate.position = currentPosition;
props.reorderEntryFunc(
[...listEntries].sort((a: ReorderableEntry<T>, b: ReorderableEntry<T>) => a.position - b.position),
);
}
async function setIsSelected(val: boolean) {
_setIsSelected(val);
// Wait 3 frames, then set. I have no idea why, but if you dont wait long enough it doesn't work.
for (let i = 0; i < 3; i++) await new Promise((res) => requestAnimationFrame(res));
setIsSelectedLastFrame(val);
}
return (
<div
style={
props.animate
? {
transition:
isSelected || isSelectedLastFrame
? ''
: 'transform 0.3s cubic-bezier(0.25, 1, 0.5, 1), opacity 0.3s cubic-bezier(0.25, 1, 0.5, 1)', // easeOutQuart https://easings.net/#easeOutQuart
transform: !props.reorderEnabled || isSelected ? 'scale(1)' : 'scale(0.9)',
opacity: !props.reorderEnabled || isSelected ? 1 : 0.7,
}
: {}
}
>
<Field
label={props.entryData.label}
{...props.fieldProps}
focusable={!props.children}
onButtonDown={onReorder}
onGamepadBlur={() => setIsSelected(false)}
onGamepadFocus={() => setIsSelected(true)}
>
<Focusable style={{ display: 'flex', width: '100%', position: 'relative' }}>{props.children}</Focusable>
</Field>
</div>
);
}

View File

@@ -13,8 +13,6 @@ export const SuspensefulImage: FC<SuspensefulImageProps> = (props) => {
const [error, setError] = useState(false);
useEffect(() => {
setLoading(true);
setError(false);
const img = new Image();
img.src = props.src || '';
img.addEventListener('load', () => {
@@ -23,7 +21,7 @@ export const SuspensefulImage: FC<SuspensefulImageProps> = (props) => {
img.addEventListener('error', () => {
setError(true);
});
}, [props.src]);
}, []);
return loading ? (
<div

View File

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

View File

@@ -1,15 +1,31 @@
import { useEffect, useState } from 'react';
import { getGamepadNavigationTrees } from '../utils';
declare global {
var FocusNavController: any;
}
function getQuickAccessWindow(): Window | null {
const navTrees = getGamepadNavigationTrees();
return navTrees.find((tree: any) => tree?.id === 'QuickAccess-NA')?.m_Root?.m_element?.ownerDocument.defaultView ?? null;
try {
const context = FocusNavController?.m_ActiveContext || FocusNavController?.m_LastActiveContext;
const navTrees = context?.m_rgGamepadNavigationTrees || FocusNavController?.m_rgGamepadNavigationTrees;
return navTrees?.find((tree: any) => tree?.id === "QuickAccess-NA")?.m_Root?.m_element?.ownerDocument.defaultView ?? null;
} catch (error) {
console.error(error);
return null;
}
}
/**
* Returns state indicating the visibility of quick access menu.
*
* @returns `true` if quick access menu is visible and `false` otherwise.
* @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";
@@ -37,10 +53,7 @@ function getQuickAccessWindow(): Window | null {
* };
*/
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);
const [isVisible, setIsVisible] = useState(getQuickAccessWindow()?.document.hasFocus() ?? true);
useEffect(() => {
const quickAccessWindow = getQuickAccessWindow();
@@ -49,12 +62,16 @@ export function useQuickAccessVisible(): boolean {
return;
}
const onVisibilityChange = () => setIsHidden(quickAccessWindow.document.hidden);
quickAccessWindow.addEventListener('visibilitychange', onVisibilityChange);
const onBlur = () => setIsVisible(false);
const onFocus = () => setIsVisible(true);
quickAccessWindow.addEventListener('blur', onBlur);
quickAccessWindow.addEventListener('focus', onFocus);
return () => {
quickAccessWindow.removeEventListener('visibilitychange', onVisibilityChange);
quickAccessWindow.removeEventListener('blur', onBlur);
quickAccessWindow.removeEventListener('focus', onFocus);
};
}, []);
return !isHidden;
return isVisible;
}

View File

@@ -7,10 +7,9 @@ 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>);
export const ButtonItem = Object.values(CommonUIModule).find(
(mod: any) =>
mod?.render?.toString()?.includes('"highlightOnFocus","childrenContainerWidth"') ||
mod?.render?.toString()?.includes('childrenContainerWidth:"min"'),
) as FC<ButtonItemProps>;

View File

@@ -8,5 +8,4 @@ export interface ItemProps {
bottomSeparator?: 'standard' | 'thick' | 'none';
indentLevel?: number;
tooltip?: string;
highlightOnFocus?: boolean;
}

View File

@@ -1,4 +1,4 @@
import { CSSProperties, FC } from 'react';
import { CSSProperties, FC, ReactNode } from 'react';
import { findModuleChild } from '../webpack';
@@ -12,7 +12,7 @@ export interface MarqueeProps {
resetOnPause?: boolean;
style?: CSSProperties;
className?: string;
children: React.ReactNode;
children: ReactNode;
}
export const Marquee: FC<MarqueeProps> = findModuleChild((m) => {

View File

@@ -41,8 +41,8 @@ export const MenuGroup: FC<MenuGroupProps> = findModuleChild((m) => {
for (let prop in m) {
if (
(m[prop]?.toString()?.includes?.('bInGamepadUI:') &&
fakeRenderComponent(() => m[prop]({overview: {appid: 7}}))?.type?.prototype?.RenderSubMenu) ||
(m[prop]?.toString()?.includes('bInGamepadUI:') &&
fakeRenderComponent(() => m[prop]())?.type?.prototype?.RenderSubMenu) ||
(m[prop]?.prototype?.RenderSubMenu && m[prop]?.prototype?.ShowSubMenu)
) {
return m[prop];

View File

@@ -123,7 +123,7 @@ export const ModalRoot = (Object.values(
if (typeof m !== 'object') return false;
for (let prop in m) {
if (m[prop]?.m_mapModalManager && Object.values(m)?.find((x: any) => x?.type)) {
if (m[prop]?.m_mapModalManager) {
return true;
}
}
@@ -154,16 +154,3 @@ export const ModalRoot = (Object.values(
}
}
})) 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,16 +1,10 @@
import { FC, ReactNode } from 'react';
import { FC } 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) => {
@@ -24,10 +18,6 @@ const [panelSection, mod] = findModuleChild((mod: any) => {
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>);
export const PanelSectionRow = Object.values(mod).filter(
(exp: any) => !exp?.toString()?.includes('.PanelSection'),
)[0] as FC;

View File

@@ -1,4 +1,3 @@
import { sleep } from '../utils';
import { Module, findModuleChild } from '../webpack';
export enum SideMenu {
@@ -15,8 +14,7 @@ export enum QuickAccessTab {
Settings,
Perf,
Help,
Music,
Decky = 999,
Decky,
}
export enum DisplayStatus {
@@ -78,6 +76,7 @@ export interface WindowRouter {
NavigateToChat(): void;
NavigateToSteamWeb(url: string): void;
NavigateBack(): void;
NavigateToWebRoute(unknown?: any, unknown2?: any): void;
}
export interface WindowStore {
@@ -118,6 +117,7 @@ export interface Navigation {
NavigateToLibraryTab(): void;
NavigateToLayoutPreview(e: unknown): void;
NavigateToSteamWeb(url: string): void;
NavigateToWebRoute(unknown?: any, unknown2?: any): void;
OpenSideMenu(sideMenu: SideMenu): void;
OpenQuickAccessMenu(quickAccessTab?: QuickAccessTab): void;
OpenMainMenu(): void;
@@ -125,62 +125,32 @@ export interface Navigation {
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);
}
export const Navigation = {
Navigate: Router.Navigate.bind(Router),
NavigateBack: Router.WindowStore?.GamepadUIMainWindowInstance?.NavigateBack.bind(
Router.WindowStore.GamepadUIMainWindowInstance,
),
NavigateToAppProperties: Router.NavigateToAppProperties.bind(Router),
NavigateToExternalWeb: Router.NavigateToExternalWeb.bind(Router),
NavigateToInvites: Router.NavigateToInvites.bind(Router),
NavigateToChat: Router.NavigateToChat.bind(Router),
NavigateToLibraryTab: Router.NavigateToLibraryTab.bind(Router),
NavigateToLayoutPreview: Router.NavigateToLayoutPreview.bind(Router),
NavigateToSteamWeb: Router.WindowStore?.GamepadUIMainWindowInstance?.NavigateToSteamWeb.bind(
Router.WindowStore.GamepadUIMainWindowInstance,
),
NavigateToWebRoute: Router.WindowStore?.GamepadUIMainWindowInstance?.NavigateToWebRoute.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;

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

@@ -3,7 +3,7 @@ import { ReactNode, VFC } from 'react';
import { Module, findModuleChild } from '../webpack';
export interface SidebarNavigationPage {
title: ReactNode;
title: string;
content: ReactNode;
icon?: ReactNode;
visible?: boolean;
@@ -16,7 +16,7 @@ export interface SidebarNavigationPage {
export interface SidebarNavigationProps {
title?: string;
pages: (SidebarNavigationPage | 'separator')[];
pages: SidebarNavigationPage[];
showTitle?: boolean;
disableRouteReporting?: boolean;
page?: string;

View File

@@ -25,7 +25,6 @@ export interface SliderFieldProps extends ItemProps {
valueSuffix?: string;
minimumDpadGranularity?: number;
onChange?(value: number): void;
className?: string;
}
export const SliderField = Object.values(CommonUIModule).find((mod: any) =>

View File

@@ -217,14 +217,6 @@ export type AppLanguages = {
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;
@@ -300,18 +292,9 @@ export interface AppDetails {
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

@@ -5,9 +5,7 @@ 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')
)
if (m[prop]?.toString()?.includes('Steam Spinner') && m[prop].toString().includes('PreloadThrobber'))
return m[prop];
}
}) as FC<SVGAttributes<SVGElement>>;

View File

@@ -4,7 +4,6 @@ import { CommonUIModule } from '../webpack';
import { ItemProps } from './Item';
export interface ToggleFieldProps extends ItemProps {
highlightOnFocus?: boolean;
checked: boolean;
disabled?: boolean;
onChange?(checked: boolean): void;

View File

@@ -25,58 +25,9 @@ export * from './TextField';
export * from './Toggle';
export * from './ToggleField';
export * from './SteamClient';
export * from './Scroll';
import { AppDetails, LogoPosition, SteamAppOverview, SteamClient } from './SteamClient';
import {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;
};
}
var SteamClient: SteamClient;
}

View File

@@ -441,232 +441,6 @@ type AppDetailsClasses = Record<
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
>;
type GamepadTabbedPageClasses = Record<
| 'duration-app-launch'
| 'headerHeight'
| 'contentPadding'
| 'GamepadTabbedPage'
| 'TabHeaderRowWrapper'
| 'Floating'
| 'TabRow'
| 'TabRowTabs'
| 'BleedGlyphs'
| 'TabsRowScroll'
| 'FixCenterAlignScroll'
| 'Tab'
| 'Selected'
| 'HasAddon'
| 'RightAddon'
| 'TabTitle'
| 'LeftAddon'
| 'TabCount'
| 'Active'
| 'TabBadge'
| 'TabCountBadge'
| 'TabRowSpacer'
| 'Glyphs'
| 'Show'
| 'TabContents'
| 'ContentTransition'
| 'TabContentsScroll'
| 'Right'
| 'Enter'
| 'EnterActive'
| 'Exit'
| 'ExitActive'
| 'Left'
| 'TabIcon',
string
>;
type GamepadContextMenuClasses = Record<
| "duration-app-launch"
| "BasicContextMenuModal"
| "BasicContextMenuHeader"
| "BasicContextMenuHeaderShrinkableSpacing"
| "BasicContextMenuContainer"
| "slideInAnimation"
| "contextMenu"
| "contextMenuContents"
| "hasSubMenu"
| "contextMenuFade"
| "contextMenuItem"
| "active"
| "Selected"
| "Focused"
| "Positive"
| "Emphasis"
| "Destructive"
| "Capitalized"
| "MenuSectionHeader"
| "UpperCase"
| "SubMenu"
| "ContextMenuSeparator"
| "Label"
| "Arrow"
| "ItemFocusAnim-darkerGrey-nocolor"
| "ItemFocusAnim-darkerGrey"
| "ItemFocusAnim-darkGrey"
| "ItemFocusAnim-grey"
| "ItemFocusAnimBorder-darkGrey"
| "ItemFocusAnim-green"
| "focusAnimation"
| "hoverAnimation",
string
>;
type AchievementClasses = Record<
| "nAchievementHeight"
| "nGlobalAchievementHeight"
| "nAchievementsListTitleHeight"
| "nAchievementGap"
| "AchievementList"
| "ListTitle"
| "AchievementListItemBase"
| "Container"
| "Content"
| "Right"
| "Footer"
| "AchievementTitle"
| "AchievementDescription"
| "AchievementGlobalPercentage"
| "InBody"
| "VerticalContent"
| "UnlockDate"
| "AlignEnd"
| "ProgressBar"
| "ProgressCount"
| "AchievementContent"
| "HiddenAchievementContent"
| "FriendAchievementFooter"
| "GlobalPercentage"
| "UserUnlockDateTime"
| "GlobalAchievementsListHeader"
| "SearchField"
| "Avatar"
| "HeaderText"
| "GlobalAchievementListItem"
| "UnlockContainer"
| "Info"
| "Title"
| "Description"
| "Percent"
| "ImageContainer"
| "ProgressFill"
| "SpoilerWarning"
| "Hidden"
| "ComparisonAchieverColumn"
| "ComparisonAchieverInfo"
| "ProgressContainer"
| "ProgressLabel"
| "Secondary"
| "AvatarContainer"
| "Unachieved",
string
>;
type MainMenuAppRunningClasses = Record<
| "duration-app-launch"
| "ScrollMask"
| "HideMask"
| "MainMenuAppRunning"
| "MenuOpen"
| "NavigationColumn"
| "ControllerColumnFocused"
| "NavColumnFocused"
| "NavigationBox"
| "NavigationMenuItem"
| "ItemFocusAnim-darkerGrey"
| "Active"
| "Disabled"
| "SwitchAppsTitle"
| "SelectableAppWindow"
| "ActiveDot"
| "NavigationMenuItemSeparator"
| "AppColumn"
| "FocusedColumn"
| "AppColumnContent"
| "ActiveContent"
| "CurrentGameBackground"
| "CurrentGameLogo"
| "OverlayAchievements"
| "Container"
| "OverlayGuides"
| "OverlayNotes"
| "OverlayInplaceBrowser"
| "ItemFocusAnim-darkerGrey-nocolor"
| "ItemFocusAnim-darkGrey"
| "ItemFocusAnim-grey"
| "ItemFocusAnimBorder-darkGrey"
| "ItemFocusAnim-green"
| "focusAnimation"
| "hoverAnimation",
string
>;
export const quickAccessMenuClasses: QuickAccessMenuClasses = findModule(
(mod) => typeof mod === 'object' && mod?.Title?.includes('quickaccessmenu'),
);
@@ -685,7 +459,7 @@ 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'),
(mod) => typeof mod === 'object' && mod?.PanelSection?.includes('quickaccesscontrols'),
);
export const updaterFieldClasses: UpdaterFieldClasses = findModule(
(mod) => typeof mod === 'object' && mod?.OOBEUpdateStatusContainer?.includes('updaterfield'),
@@ -702,18 +476,3 @@ export const appDetailsHeaderClasses: AppDetailsHeaderClasses = findModule(
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_'),
);
export const gamepadTabbedPageClasses: GamepadTabbedPageClasses = findModule(
(mod) => typeof mod === 'object' && mod?.GamepadTabbedPage?.includes('gamepadtabbedpage_')
);
export const gamepadContextMenuClasses: GamepadContextMenuClasses = findModule(
(mod) => typeof mod === 'object' && mod?.BasicContextMenuModal?.includes('gamepadcontextmenu')
);
export const achievementClasses: AchievementClasses = findModule(
(mod) => typeof mod === 'object' && mod?.AchievementListItemBase?.includes('achievementslist')
);
export const mainMenuAppRunningClasses: MainMenuAppRunningClasses = findModule(
(mod) => typeof mod === 'object' && mod?.MainMenuAppRunning?.includes('mainmenuapprunning')
);

View File

@@ -21,6 +21,52 @@ interface ServerResponseError {
export type ServerResponse<TRes> = ServerResponseSuccess<TRes> | ServerResponseError;
export interface MainMenuItem {
/** Called when clicking this menu item, useful if you do not want to navigate to a route.
* For navigating to a route, use route instead. */
action?: () => void;
/** The route this menu item navigates to @example "/steamweb" */
route?: string;
/** Props for the route this item navigates to. Currently only used for /steamweb to determine what webpack to navigate to @example {url: 'https://store.steampowered.com/'} */
routeState?: any;
/** Label for this menu item, @example "Store" */
label: ReactNode;
/** Runs when this item is selected. Defaults to setting the focused app to null.
* If you override this you must also call the original to retain proper behaivour */
onFocus?: () => void;
/** Icon of this menu item, you probably want a react SVG node */
children: ReactNode;
}
export interface CustomMainMenuItem extends MainMenuItem {
index: number;
}
export interface MainMenuItemReactNode {
props: MainMenuItem;
type: ComponentType;
key: any;
}
export interface OverlayReactNode {
props: MainMenuItem;
type: ComponentType;
}
export type ItemPatch = (item: MainMenuItemReactNode) => MainMenuItemReactNode;
export type OverlayPatch = (overlay: OverlayReactNode) => OverlayReactNode;
export interface MenuHook {
addItem(item: CustomMainMenuItem): CustomMainMenuItem;
addPatch(path: string, patch: ItemPatch): ItemPatch;
addOverlayPatch(patch: OverlayPatch): OverlayPatch;
addOverlayComponent(component: ReactNode): ReactNode;
removePatch(path: string, patch: ItemPatch): void;
removeOverlayPatch(patch: OverlayPatch): void;
removeOverlayComponent(component: ReactNode): void;
removeItem(item: CustomMainMenuItem): void;
}
export type RoutePatch = (route: RouteProps) => RouteProps;
export interface RouterHook {
@@ -57,26 +103,11 @@ export interface FilePickerRes {
realpath: string;
}
export const enum FileSelectionType {
FILE,
FOLDER,
}
export interface ServerAPI {
routerHook: RouterHook;
menuHook: MenuHook;
toaster: Toaster;
openFilePicker(startPath: string, includeFiles?: boolean, regex?: RegExp): Promise<FilePickerRes>;
openFilePickerV2(
select: FileSelectionType,
startPath: string,
includeFiles?: boolean,
includeFolders?: boolean,
filter?: RegExp | ((file: File) => boolean),
extensions?: string[],
showHiddenFiles?: boolean,
allowAllFiles?: boolean,
max?: number,
): Promise<FilePickerRes>;
callPluginMethod<TArgs = {}, TRes = {}>(methodName: string, args: TArgs): Promise<ServerResponse<TRes>>;
callServerMethod<TArgs = {}, TRes = {}>(methodName: string, args: TArgs): Promise<ServerResponse<TRes>>;
fetchNoCors<TRes = {}>(url: RequestInfo, request?: RequestInit): Promise<ServerResponse<TRes>>;

View File

@@ -1,11 +1,6 @@
export * from './patcher';
export * from './react';
declare global {
var FocusNavController: any;
var GamepadNavTree: any;
}
export function joinClassNames(...classes: string[]): string {
return classes.join(' ');
}
@@ -21,22 +16,7 @@ export function findSP(): Window {
// old (SP as host)
if (document.title == 'SP') return window;
// new (SP as popup)
const navTrees = getGamepadNavigationTrees();
return navTrees?.find((x: any) => x.m_ID == 'root_1_').Root.Element.ownerDocument.defaultView;
}
/**
* Gets the correct FocusNavController, as the Feb 22 2023 beta has two for some reason.
*/
export function getFocusNavController(): any {
return window.GamepadNavTree?.m_context?.m_controller || window.FocusNavController;
}
/**
* Gets the gamepad navigation trees as Valve seems to be moving them.
*/
export function getGamepadNavigationTrees(): any {
const focusNav = getFocusNavController();
const context = focusNav.m_ActiveContext || focusNav.m_LastActiveContext;
return context?.m_rgGamepadNavigationTrees;
const context = FocusNavController.m_ActiveContext || FocusNavController.m_LastActiveContext;
return context.m_rgGamepadNavigationTrees.find((x: any) => x.m_ID == 'root_1_').Root
.Element.ownerDocument.defaultView;
}

View File

@@ -46,21 +46,13 @@ export function fakeRenderComponent(fun: Function, customHooks: any = {}): any {
}
export function wrapReactType(node: any, prop: any = 'type') {
if (node[prop]?.__DECKY_WRAPPED) {
return node[prop];
} else {
return (node[prop] = { ...node[prop], __DECKY_WRAPPED: true });
}
return (node[prop] = { ...node[prop] });
}
export function wrapReactClass(node: any, prop: any = 'type') {
if (node[prop]?.__DECKY_WRAPPED) {
return node[prop];
} else {
const cls = node[prop];
const wrappedCls = class extends cls { static __DECKY_WRAPPED = true; };
return (node[prop] = wrappedCls);
}
const cls = node[prop];
const wrappedCls = class extends cls {};
return (node[prop] = wrappedCls);
}
export function getReactInstance(o: HTMLElement | Element | Node) {

View File

@@ -37,11 +37,7 @@ if (window.webpackJsonp && !window.webpackJsonp.deckyShimmed) {
},
]);
for (let i of Object.keys(initReq.m)) {
try {
webpackCache[i] = initReq(i);
} catch (e) {
console.debug("[DFL:Webpack]: Ignoring require error for module", i, e);
}
webpackCache[i] = initReq(i);
}
}

View File

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