Compare commits

...

39 Commits

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

* request param should be optional
2022-06-28 21:12:44 -04:00
semantic-release-bot
2c7d266c81 chore(release): 1.0.0 [CI SKIP] 2022-06-23 02:58:38 +00:00
AAGaming
9bd96227a0 refactor(components): rename Field components
BREAKING CHANGE: Toggle -> ToggleField Slider -> SliderField & add Toggle component
2022-06-22 22:57:51 -04:00
semantic-release-bot
ddafa5666d chore(release): 0.12.3 [CI SKIP] 2022-06-23 02:45:11 +00:00
AAGaming
46977496fd fix(Field): description is a string you idiot 2022-06-22 22:44:13 -04:00
semantic-release-bot
71a6c79384 chore(release): 0.12.2 [CI SKIP] 2022-06-23 01:53:07 +00:00
AAGaming
da4c79b5ae fix(Field): title -> label 2022-06-22 21:52:17 -04:00
semantic-release-bot
7b04bf5827 chore(release): 0.12.1 [CI SKIP] 2022-06-23 01:47:38 +00:00
AAGaming
a87e1bb46f fix(components): export FIeld 2022-06-22 21:46:50 -04:00
semantic-release-bot
b6e2e7e4dc chore(release): 0.12.0 [CI SKIP] 2022-06-23 01:45:47 +00:00
AAGaming
7d82a82e9c feat(components): add Field 2022-06-22 21:44:46 -04:00
semantic-release-bot
0cee60d122 chore(release): 0.11.1 [CI SKIP] 2022-06-23 00:42:16 +00:00
AAGaming
7efc0347f7 fix(Router): make specifying quick access tab not required 2022-06-22 20:41:22 -04:00
semantic-release-bot
d227858e62 chore(release): 0.11.0 [CI SKIP] 2022-06-20 21:37:03 +00:00
AAGaming
db64f34725 feat(utils): add sleep util 2022-06-20 17:35:56 -04:00
14 changed files with 352 additions and 20 deletions

View File

@@ -1,3 +1,134 @@
# [1.5.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.4.0...v1.5.0) (2022-08-10)
### Features
* **ServerAPI:** add Toaster to serverAPI ([e2126af](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/e2126afd06f339a22dbbaea89b834157a5975b96))
# [1.4.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.3.0...v1.4.0) (2022-08-08)
### Features
* **utils:** add findInTree and findInReactTree ([b21dfcd](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/b21dfcdb661fd7ad43213756dadb6cfdf0ac1e94))
# [1.3.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.2.4...v1.3.0) (2022-08-02)
### Features
* **plugin:** api for patching existing routes ([5b29447](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/5b29447cfa597773a81aa233da9362346683505d))
## [1.2.4](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.2.3...v1.2.4) (2022-07-25)
### Bug Fixes
* **Modal:** add closeModal ([994b9e2](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/994b9e2cc6f41da3d813e6f339bd2fd30e4fa3ad))
## [1.2.3](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.2.2...v1.2.3) (2022-07-25)
### Bug Fixes
* **Modal:** add another prop ([2fdfcdd](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/2fdfcdd4788ea0d6483e92729c3102212f3ec0fb))
## [1.2.2](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.2.1...v1.2.2) (2022-07-25)
### Bug Fixes
* **Modal:** add more props to typings ([97997ad](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/97997adfaf1a68ef436279e6e48f34f5eaa9531c))
## [1.2.1](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.2.0...v1.2.1) (2022-07-13)
### Bug Fixes
* **ProgressBar:** extend correct prop type ([944a902](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/944a9024ff20f0b596869564d014d7dd73e74254))
# [1.2.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.1.0...v1.2.0) (2022-07-13)
### Features
* **ProgressBar:** add new progress bars ([d9e4ff3](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/d9e4ff3ebd22a31306f564e7f8ad82879c8fb699))
# [1.1.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.0.2...v1.1.0) (2022-07-09)
### Features
* **Router.tsx:** adds more methods to router ([#9](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/9)) ([602f93a](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/602f93ae6a5ceca5383b888cd4803638799558c5))
## [1.0.2](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.0.1...v1.0.2) (2022-07-07)
### Bug Fixes
* **Plugin:** support non-ui plugins ([51c418d](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/51c418d560247c917125cd5534a978256724e5f3))
## [1.0.1](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v1.0.0...v1.0.1) (2022-06-29)
### Bug Fixes
* **package:** enable tree shaking ([58933f8](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/58933f827ce2e2ae9b162da4e0061a7591c5759d))
# [1.0.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v0.12.3...v1.0.0) (2022-06-23)
### Code Refactoring
* **components:** rename Field components ([9bd9622](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/9bd96227a0bb295dcc29abca71e37983307f0505))
### BREAKING CHANGES
* **components:** Toggle -> ToggleField Slider -> SliderField & add Toggle component
## [0.12.3](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v0.12.2...v0.12.3) (2022-06-23)
### Bug Fixes
* **Field:** description is a string you idiot ([4697749](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/46977496fd4cbe266c370fcffe59a9d9b7543a92))
## [0.12.2](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v0.12.1...v0.12.2) (2022-06-23)
### Bug Fixes
* **Field:** title -> label ([da4c79b](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/da4c79b5aeb3c589527e17ad29610a8e3f929b79))
## [0.12.1](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v0.12.0...v0.12.1) (2022-06-23)
### Bug Fixes
* **components:** export FIeld ([a87e1bb](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/a87e1bb46f749e10ea2b94a011df48f162834c25))
# [0.12.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v0.11.1...v0.12.0) (2022-06-23)
### Features
* **components:** add Field ([7d82a82](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/7d82a82e9c4db59832593cb6f0f78775b252dc69))
## [0.11.1](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v0.11.0...v0.11.1) (2022-06-23)
### Bug Fixes
* **Router:** make specifying quick access tab not required ([7efc034](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/7efc0347f7aa22773feccb0763280c4fd1c4a231))
# [0.11.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v0.10.5...v0.11.0) (2022-06-20)
### Features
* **utils:** add sleep util ([db64f34](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/db64f3472542b080b1d470c6b8d7aa441db0bfe6))
## [0.10.5](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v0.10.4...v0.10.5) (2022-06-20)

View File

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

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "decky-frontend-lib",
"version": "0.10.5",
"version": "1.5.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "decky-frontend-lib",
"version": "0.10.5",
"version": "1.5.0",
"hasInstallScript": true,
"license": "GPL-2.0-or-later",
"devDependencies": {

View File

@@ -1,10 +1,11 @@
{
"name": "decky-frontend-lib",
"version": "0.10.5",
"version": "1.5.0",
"description": "A library for building decky plugins",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"module": "dist/index.js",
"sideEffects": false,
"scripts": {
"build": "shx rm -rf dist && tsc -b",
"dev": "tsc -b -w",

View File

@@ -0,0 +1,22 @@
import { FC, HTMLAttributes, ReactNode, RefAttributes } from 'react';
import { findModuleChild } from '../webpack';
export interface FieldProps extends HTMLAttributes<HTMLDivElement> {
label?: string | ReactNode;
description?: string | ReactNode;
disabled?: boolean;
icon?: ReactNode;
childrenLayout?: string;
childrenContainerWidth?: string;
padding?: string;
highlightOnFocus?: boolean;
indentLevel?: number;
verticalAlignment?: string;
}
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>>;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,7 @@
export * from './Button';
export * from './ButtonItem';
export * from './Dropdown';
export * from './Field';
export * from './Focusable';
export * from './Menu';
export * from './Modal';
@@ -8,9 +9,10 @@ export * from './Panel';
export * from './ProgressBar';
export * from './Router';
export * from './SidebarNavigation';
export * from './Slider';
export * from './SliderField';
export * from './Spinner';
export * from './static-classes';
export * from './SteamSpinner';
export * from './TextField';
export * from './Toggle';
export * from './ToggleField';

View File

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

View File

@@ -88,4 +88,37 @@ export function getReactInstance(o: HTMLElement | Element | Node) {
export function joinClassNames(...classes: string[]): string {
return classes.join(" ");
}
}
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' ]
});