mirror of
https://github.com/SteamDeckHomebrew/decky-frontend-lib.git
synced 2026-05-20 10:00:08 +02:00
Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3f126fe471 | ||
|
|
e644de35d7 | ||
|
|
9b79f70ac7 | ||
|
|
d237bd48e4 | ||
|
|
f7d73b4dc3 | ||
|
|
9edb1e6b97 | ||
|
|
605e4f5eae | ||
|
|
e2f675835e | ||
|
|
b7dc1d6275 | ||
|
|
a1fdae9cd2 | ||
|
|
7cf45cf371 | ||
|
|
df1c8dbb8b | ||
|
|
1de979f713 | ||
|
|
f23070a82a | ||
|
|
b87e7c2f40 | ||
|
|
6db29a9a6e | ||
|
|
e2126afd06 | ||
|
|
cea587958e | ||
|
|
b21dfcdb66 | ||
|
|
d46251bb91 | ||
|
|
5b29447cfa | ||
|
|
e993d06963 | ||
|
|
a3c136a07e | ||
|
|
994b9e2cc6 | ||
|
|
801d70f300 | ||
|
|
2fdfcdd478 | ||
|
|
9a728611f4 | ||
|
|
97997adfaf |
@@ -1,7 +1,15 @@
|
||||
{
|
||||
"branches": ["main", "dev"],
|
||||
"plugins": [
|
||||
"@semantic-release/commit-analyzer",
|
||||
[
|
||||
"@semantic-release/commit-analyzer",
|
||||
{
|
||||
"preset": "angular",
|
||||
"releaseRules": [
|
||||
{"type": "chore", "scope": "classes", "release": "patch"}
|
||||
]
|
||||
}
|
||||
],
|
||||
"@semantic-release/release-notes-generator",
|
||||
"@semantic-release/changelog",
|
||||
"@semantic-release/npm",
|
||||
|
||||
79
CHANGELOG.md
79
CHANGELOG.md
@@ -1,3 +1,82 @@
|
||||
## [1.7.1](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.7.0...v1.7.1) (2022-08-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **utils:** better method to wrap react classes ([e644de3](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/e644de35d70680d17d70e89cc9b929a5aae08b48))
|
||||
|
||||
# [1.7.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.6.2...v1.7.0) (2022-08-17)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **utils:** add wrapReactClass ([d237bd4](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/d237bd48e4b9e6436d7daefdf70327875e9e940d))
|
||||
|
||||
## [1.6.2](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.6.1...v1.6.2) (2022-08-15)
|
||||
|
||||
## [1.6.1](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.6.0...v1.6.1) (2022-08-13)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **wrapReactType:** try another method ([b7dc1d6](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/b7dc1d6275ed28b1e37b6cb512b2c5d1600a8f63))
|
||||
|
||||
# [1.6.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.5.1...v1.6.0) (2022-08-13)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Utilities:** add wrapReactType utility ([7cf45cf](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/7cf45cf3718d6e5295115f28a5f4dd24c6ff14e3))
|
||||
|
||||
## [1.5.1](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.5.0...v1.5.1) (2022-08-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **security:** update for minimist pollution exploit ([1de979f](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/1de979f7135c8d5eea1faca3d480d662c5e41d3d))
|
||||
|
||||
# [1.5.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.4.0...v1.5.0) (2022-08-10)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **ServerAPI:** add Toaster to serverAPI ([e2126af](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/e2126afd06f339a22dbbaea89b834157a5975b96))
|
||||
|
||||
# [1.4.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.3.0...v1.4.0) (2022-08-08)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **utils:** add findInTree and findInReactTree ([b21dfcd](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/b21dfcdb661fd7ad43213756dadb6cfdf0ac1e94))
|
||||
|
||||
# [1.3.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.2.4...v1.3.0) (2022-08-02)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **plugin:** api for patching existing routes ([5b29447](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/5b29447cfa597773a81aa233da9362346683505d))
|
||||
|
||||
## [1.2.4](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.2.3...v1.2.4) (2022-07-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Modal:** add closeModal ([994b9e2](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/994b9e2cc6f41da3d813e6f339bd2fd30e4fa3ad))
|
||||
|
||||
## [1.2.3](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.2.2...v1.2.3) (2022-07-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Modal:** add another prop ([2fdfcdd](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/2fdfcdd4788ea0d6483e92729c3102212f3ec0fb))
|
||||
|
||||
## [1.2.2](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.2.1...v1.2.2) (2022-07-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Modal:** add more props to typings ([97997ad](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/97997adfaf1a68ef436279e6e48f34f5eaa9531c))
|
||||
|
||||
## [1.2.1](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.2.0...v1.2.1) (2022-07-13)
|
||||
|
||||
|
||||
|
||||
26
README.md
26
README.md
@@ -1 +1,27 @@
|
||||
# Decky Frontend Library
|
||||
|
||||
Library used to develop plugins used for use with [decky-loader](https://github.com/SteamDeckHomebrew/decky-loader).
|
||||
|
||||
## Decky Loader Discord [](https://discord.gg/ZU74G2NJzk)
|
||||
|
||||
Please contact the developers here for questions and support that cannot be addressed via a Github issue.
|
||||
|
||||
## Developers and Contributors
|
||||
|
||||
This library is focused on usage by developers to provide custom React components based on those found in the Steam Deck's React UI.
|
||||
This method allows developers to add UI elements and code without clobbering the existing UI of the deck in order to do so.
|
||||
This library can also theoretically be used to extend existing UI elements of the Steam Deck UI but this has not been tested extensively.
|
||||
|
||||
### Getting Started (Contributors)
|
||||
|
||||
1. Clone the repository to your preferred location
|
||||
2. If you wish to add features such as new UI components, please create a feature branch to PR to the original repo.
|
||||
3. Bug/hotfixes are acceptable on the main branch,
|
||||
|
||||
### Getting Started (Developers)
|
||||
|
||||
If you would like a feature added to decky-frontend-lib, please request it via a Github issue.
|
||||
|
||||
If you want to start making a plugin with decky-frontend-lib, please direct your attention to the [decky-plugin-template](https://github.com/SteamDeckHomebrew/decky-plugin-template) repository.
|
||||
|
||||
This library can be found on [npm](https://www.npmjs.com/package/decky-frontend-lib) and as such you can pull it without a local copy for your project as needed.
|
||||
|
||||
946
package-lock.json
generated
946
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "decky-frontend-lib",
|
||||
"version": "1.2.1",
|
||||
"version": "1.7.1",
|
||||
"description": "A library for building decky plugins",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
@@ -54,7 +54,7 @@
|
||||
"import-sort-style-module": "^6.0.0",
|
||||
"jest": "^27.5.1",
|
||||
"prettier-plugin-import-sort": "^0.0.7",
|
||||
"semantic-release": "^19.0.2",
|
||||
"semantic-release": "^19.0.3",
|
||||
"shx": "^0.3.4",
|
||||
"ts-jest": "^27.1.4",
|
||||
"typescript": "^4.6.3"
|
||||
@@ -72,5 +72,8 @@
|
||||
"style": "module",
|
||||
"parser": "typescript"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"minimist": "^1.2.6"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,15 @@ export interface ModalRootProps {
|
||||
onMiddleButton?(): void;
|
||||
onCancel?(): void;
|
||||
onOK?(): void;
|
||||
onEscKeypress?(): void;
|
||||
closeModal?(): void;
|
||||
className?: string;
|
||||
modalClassName?: string;
|
||||
bAllowFullSize?: boolean;
|
||||
bDestructiveWarning?: boolean;
|
||||
bDisableBackgroundDismiss?: boolean;
|
||||
bHideCloseIcon?: boolean;
|
||||
bOKDisabled?: boolean;
|
||||
}
|
||||
|
||||
export const ModalRoot = findModuleChild((m) => {
|
||||
|
||||
@@ -63,6 +63,14 @@ type StaticClasses = Record<
|
||||
string
|
||||
>;
|
||||
|
||||
type ScrollClasses = Record<
|
||||
| 'ScrollBoth'
|
||||
| 'ScrollPanel'
|
||||
| 'ScrollX'
|
||||
| 'ScrollY',
|
||||
string
|
||||
>;
|
||||
|
||||
type GamepadDialogClasses = Record<
|
||||
| 'duration-app-launch'
|
||||
| 'GamepadDialogContent'
|
||||
@@ -167,6 +175,16 @@ export const staticClasses: StaticClasses = findModule((mod) => {
|
||||
return false;
|
||||
});
|
||||
|
||||
export const scrollClasses: ScrollClasses = findModule((mod) => {
|
||||
if (typeof mod !== 'object') return false;
|
||||
|
||||
if (mod.ScrollPanel && mod.ScrollY) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
export const gamepadDialogClasses: GamepadDialogClasses = findModule((mod) => {
|
||||
if (typeof mod !== 'object') return false;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ComponentType } from 'react';
|
||||
import type { ComponentType, ReactNode } from 'react';
|
||||
import { RouteProps } from 'react-router';
|
||||
|
||||
export interface Plugin {
|
||||
@@ -20,13 +20,34 @@ interface ServerResponseError {
|
||||
|
||||
type ServerResponse<TRes> = ServerResponseSuccess<TRes> | ServerResponseError;
|
||||
|
||||
interface RouterHook {
|
||||
type RoutePatch = (route: RouteProps) => RouteProps;
|
||||
|
||||
export interface RouterHook {
|
||||
addRoute(path: string, component: ComponentType, props?: Omit<RouteProps, 'path' | 'children'>): void;
|
||||
addPatch(path: string, patch: RoutePatch): RoutePatch;
|
||||
removePatch(path: string, patch: RoutePatch): void;
|
||||
removeRoute(path: string): void;
|
||||
}
|
||||
|
||||
export interface ToastData {
|
||||
title: ReactNode;
|
||||
body: ReactNode;
|
||||
onClick?: () => void;
|
||||
logo?: ReactNode;
|
||||
icon?: ReactNode;
|
||||
className?: string;
|
||||
contentClassName?: string;
|
||||
duration?: number
|
||||
critical?: boolean
|
||||
}
|
||||
|
||||
export interface Toaster {
|
||||
toast(toast: ToastData): void;
|
||||
}
|
||||
|
||||
export interface ServerAPI {
|
||||
routerHook: RouterHook;
|
||||
toaster: Toaster;
|
||||
callPluginMethod<TArgs = {}, TRes = {}>(methodName: string, args: TArgs): Promise<ServerResponse<TRes>>;
|
||||
callServerMethod<TArgs = {}, TRes = {}>(methodName: string, args: TArgs): Promise<ServerResponse<TRes>>;
|
||||
fetchNoCors<TRes = {}>(url: RequestInfo, request?: RequestInit): Promise<ServerResponse<TRes>>;
|
||||
|
||||
48
src/utils.ts
48
src/utils.ts
@@ -82,6 +82,23 @@ export function unpatch(obj: any, name: any): void {
|
||||
obj[name] = obj[name].__deckyOrig;
|
||||
}
|
||||
|
||||
export function wrapReactType(node: any, prop?: any) {
|
||||
return node[prop || "type"] = {...node[prop || "type"]};
|
||||
}
|
||||
|
||||
export function wrapReactClass(node: any, prop?: any) {
|
||||
const cls = node[prop || "type"];
|
||||
const wrappedCls = class extends cls {};
|
||||
Object.getOwnPropertyNames(cls.prototype).forEach((prop: any) => {
|
||||
wrappedCls.prototype[prop] = cls.prototype[prop]
|
||||
});
|
||||
Object.getOwnPropertyNames(cls).forEach((prop: any) => {
|
||||
if (prop != "prototype") wrappedCls[prop] = cls[prop]
|
||||
});
|
||||
wrappedCls.prototype.__proto__ = cls.prototype.__proto__;
|
||||
return node[prop || "type"] = wrappedCls;
|
||||
}
|
||||
|
||||
export function getReactInstance(o: HTMLElement | Element | Node) {
|
||||
return o[Object.keys(o).find(k => k.startsWith('__reactInternalInstance')) as string]
|
||||
}
|
||||
@@ -92,4 +109,33 @@ export function joinClassNames(...classes: string[]): string {
|
||||
|
||||
export function sleep(ms: number) {
|
||||
return new Promise(res => setTimeout(res, ms));
|
||||
}
|
||||
}
|
||||
|
||||
// Based on https://github.com/GooseMod/GooseMod/blob/9ef146515a9e59ed4e25665ed365fd72fc0dcf23/src/util/react.js#L20
|
||||
export interface findInTreeOpts {
|
||||
walkable?: string[],
|
||||
ignore?: string[]
|
||||
}
|
||||
|
||||
export declare type findInTreeFilter = (element: any) => boolean
|
||||
|
||||
export const findInTree = (parent: any, filter: findInTreeFilter, opts: findInTreeOpts): any => {
|
||||
const { walkable = null, ignore = [] } = opts ?? {};
|
||||
|
||||
if (!parent || typeof parent !== 'object') { // Parent is invalid to search through
|
||||
return null;
|
||||
}
|
||||
|
||||
if (filter(parent)) return parent; // Parent matches, just return
|
||||
|
||||
if (Array.isArray(parent)) { // Parent is an array, go through values
|
||||
return parent.map((x) => findInTree(x, filter, opts)).find((x) => x);
|
||||
}
|
||||
|
||||
// Parent is an object, go through values (or option to only use certain keys)
|
||||
return (walkable || Object.keys(parent)).map((x) => !ignore.includes(x) && findInTree(parent[x], filter, opts)).find((x: any) => x);
|
||||
};
|
||||
|
||||
export const findInReactTree = (node: any, filter: findInTreeFilter) => findInTree(node, filter, { // Specialised findInTree for React nodes
|
||||
walkable: [ 'props', 'children', 'child', 'sibling' ]
|
||||
});
|
||||
Reference in New Issue
Block a user