@decky/ui 4.0.2 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/dist/components/ButtonItem.js +4 -2
- package/dist/components/Dialog.js +12 -7
- package/dist/components/Dropdown.js +3 -1
- package/dist/components/Focusable.js +3 -1
- package/dist/components/Menu.js +3 -2
- package/dist/components/ProgressBar.js +3 -1
- package/dist/components/SidebarNavigation.js +3 -1
- package/dist/components/Spinner.js +1 -1
- package/dist/utils/react.d.ts +1 -0
- package/dist/utils/react.js +15 -0
- package/dist/webpack.js +1 -1
- package/package.json +26 -19
- package/src/components/ButtonItem.ts +4 -2
- package/src/components/Dialog.ts +12 -9
- package/src/components/Dropdown.ts +3 -1
- package/src/components/Focusable.ts +4 -1
- package/src/components/Menu.ts +4 -3
- package/src/components/ProgressBar.ts +3 -1
- package/src/components/SidebarNavigation.ts +3 -1
- package/src/components/Spinner.ts +2 -2
- package/src/index.ts +4 -4
- package/src/utils/react.ts +22 -0
- package/src/webpack.ts +1 -1
package/README.md
CHANGED
|
@@ -25,3 +25,6 @@ If you would like a feature added to decky-frontend-lib, please request it via a
|
|
|
25
25
|
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.
|
|
26
26
|
|
|
27
27
|
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.
|
|
28
|
+
|
|
29
|
+
Tips for fixing failing module finds after Steam updates:
|
|
30
|
+
- `Object.entries(DFL)` can point out any undefined exports
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
import { CommonUIModule } from '../webpack';
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
import { createPropListRegex } from '../utils';
|
|
3
|
+
const buttonItemRegex = createPropListRegex(["highlightOnFocus", "childrenContainerWidth"], false);
|
|
4
|
+
export const ButtonItem = Object.values(CommonUIModule).find((mod) => (mod?.render?.toString && buttonItemRegex.test(mod.render.toString())) ||
|
|
5
|
+
mod?.render?.toString?.().includes('childrenContainerWidth:"min"'));
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import { CommonUIModule } from '../webpack';
|
|
2
|
-
const CommonDialogDivs = Object.values(CommonUIModule).filter((m) => typeof m === 'object' && m?.render?.toString().includes('"div",
|
|
2
|
+
const CommonDialogDivs = Object.values(CommonUIModule).filter((m) => typeof m === 'object' && m?.render?.toString().includes('createElement("div",{...') ||
|
|
3
|
+
m?.render?.toString().includes('createElement("div",Object.assign({},'));
|
|
3
4
|
const MappedDialogDivs = new Map(Object.values(CommonDialogDivs).map((m) => {
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
try {
|
|
6
|
+
const renderedDiv = m.render({});
|
|
7
|
+
return [renderedDiv.props.className.split(' ')[0], m];
|
|
8
|
+
}
|
|
9
|
+
catch (e) {
|
|
10
|
+
console.error("[DFL:Dialog]: failed to render common dialog component", e);
|
|
11
|
+
return [null, null];
|
|
12
|
+
}
|
|
6
13
|
}));
|
|
7
14
|
export const DialogHeader = MappedDialogDivs.get('DialogHeader');
|
|
8
15
|
export const DialogSubHeader = MappedDialogDivs.get('DialogSubHeader');
|
|
@@ -12,8 +19,6 @@ export const DialogBodyText = MappedDialogDivs.get('DialogBodyText');
|
|
|
12
19
|
export const DialogBody = MappedDialogDivs.get('DialogBody');
|
|
13
20
|
export const DialogControlsSection = MappedDialogDivs.get('DialogControlsSection');
|
|
14
21
|
export const DialogControlsSectionHeader = MappedDialogDivs.get('DialogControlsSectionHeader');
|
|
15
|
-
export const DialogButtonPrimary = Object.values(CommonUIModule).find((mod) => mod?.render?.toString()?.includes('DialogButton
|
|
16
|
-
export const DialogButtonSecondary = Object.values(CommonUIModule).find((mod) => mod?.render?.toString()?.includes('
|
|
17
|
-
mod?.render?.toString()?.includes('DialogButton') &&
|
|
18
|
-
mod?.render?.toString()?.includes('Secondary'));
|
|
22
|
+
export const DialogButtonPrimary = Object.values(CommonUIModule).find((mod) => mod?.render?.toString()?.includes('"DialogButton","_DialogLayout","Primary"'));
|
|
23
|
+
export const DialogButtonSecondary = Object.values(CommonUIModule).find((mod) => mod?.render?.toString()?.includes('"DialogButton","_DialogLayout","Secondary"'));
|
|
19
24
|
export const DialogButton = DialogButtonSecondary;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
import { CommonUIModule } from '../webpack';
|
|
2
|
+
import { createPropListRegex } from '../utils';
|
|
2
3
|
export const Dropdown = Object.values(CommonUIModule).find((mod) => mod?.prototype?.SetSelectedOption && mod?.prototype?.BuildMenu);
|
|
3
|
-
|
|
4
|
+
const dropdownItemRegex = createPropListRegex(["dropDownControlRef", "description"], false);
|
|
5
|
+
export const DropdownItem = Object.values(CommonUIModule).find((mod) => mod?.toString && dropdownItemRegex.test(mod.toString()));
|
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
import { findModuleExport } from '../webpack';
|
|
2
|
-
|
|
2
|
+
import { createPropListRegex } from '../utils';
|
|
3
|
+
const focusableRegex = createPropListRegex(["flow-children", "onActivate", "onCancel", "focusClassName", "focusWithinClassName"]);
|
|
4
|
+
export const Focusable = findModuleExport((e) => e?.render?.toString && focusableRegex.test(e.render.toString()));
|
package/dist/components/Menu.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { fakeRenderComponent } from '../utils';
|
|
2
2
|
import { findModuleExport } from '../webpack';
|
|
3
|
-
export const showContextMenu = findModuleExport((e) => typeof e === 'function' && e.toString().includes('
|
|
3
|
+
export const showContextMenu = findModuleExport((e) => typeof e === 'function' && e.toString().includes('GetContextMenuManagerFromWindow(')
|
|
4
|
+
&& e.toString().includes('.CreateContextMenuInstance('));
|
|
4
5
|
export const Menu = findModuleExport((e) => e?.prototype?.HideIfSubmenu && e?.prototype?.HideMenu);
|
|
5
6
|
export const MenuGroup = findModuleExport((e) => (e?.toString()?.includes?.('bInGamepadUI:') &&
|
|
6
|
-
fakeRenderComponent(() => e({ overview: { appid: 7 } }))?.type?.prototype?.RenderSubMenu) ||
|
|
7
|
+
fakeRenderComponent(() => e({ overview: { appid: 7 } }), { useContext: () => ({ IN_GAMEPADUI: true }) })?.type?.prototype?.RenderSubMenu) ||
|
|
7
8
|
(e?.prototype?.RenderSubMenu && e?.prototype?.ShowSubMenu));
|
|
8
9
|
export const MenuItem = findModuleExport((e) => e?.render?.toString()?.includes('bPlayAudio:') || (e?.prototype?.OnOKButton && e?.prototype?.OnMouseEnter));
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { findModuleExport } from '../webpack';
|
|
2
|
+
import { createPropListRegex } from '../utils';
|
|
2
3
|
export const ProgressBar = findModuleExport((e) => e?.toString()?.includes('.ProgressBar,"standard"=='));
|
|
3
4
|
export const ProgressBarWithInfo = findModuleExport((e) => e?.toString()?.includes('.ProgressBarFieldStatus},'));
|
|
4
|
-
|
|
5
|
+
const progressBarItemRegex = createPropListRegex(["indeterminate", "nTransitionSec", "nProgress"]);
|
|
6
|
+
export const ProgressBarItem = findModuleExport((e) => e?.toString && progressBarItemRegex.test(e.toString()));
|
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
import { findModuleExport } from '../webpack';
|
|
2
|
-
|
|
2
|
+
import { createPropListRegex } from '../utils';
|
|
3
|
+
const sidebarNavigationRegex = createPropListRegex(["pages", "fnSetNavigateToPage", "disableRouteReporting"]);
|
|
4
|
+
export const SidebarNavigation = findModuleExport((e) => e?.toString && sidebarNavigationRegex.test(e.toString()));
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { IconsModule } from '../webpack';
|
|
2
|
-
export const Spinner = Object.values(IconsModule)
|
|
2
|
+
export const Spinner = Object.values(IconsModule)?.find((mod) => mod?.toString && /Spinner\)}\)?,.\.createElement\(\"path\",{d:\"M18 /.test(mod.toString()));
|
package/dist/utils/react.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ declare global {
|
|
|
4
4
|
SP_REACT: typeof React;
|
|
5
5
|
}
|
|
6
6
|
}
|
|
7
|
+
export declare function createPropListRegex(propList: string[], fromStart?: boolean): RegExp;
|
|
7
8
|
export declare function fakeRenderComponent(fun: Function, customHooks?: any): any;
|
|
8
9
|
export declare function wrapReactType(node: any, prop?: any): any;
|
|
9
10
|
export declare function wrapReactClass(node: any, prop?: any): any;
|
package/dist/utils/react.js
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
var __setFunctionName = (this && this.__setFunctionName) || function (f, name, prefix) {
|
|
2
|
+
if (typeof name === "symbol") name = name.description ? "[".concat(name.description, "]") : "";
|
|
3
|
+
return Object.defineProperty(f, "name", { configurable: true, value: prefix ? "".concat(prefix, " ", name) : name });
|
|
4
|
+
};
|
|
5
|
+
export function createPropListRegex(propList, fromStart = true) {
|
|
6
|
+
let regexString = fromStart ? "const\{" : "";
|
|
7
|
+
propList.forEach((prop, propIdx) => {
|
|
8
|
+
regexString += `"?${prop}"?:[a-zA-Z_$]{1,2}`;
|
|
9
|
+
if (propIdx < propList.length - 1) {
|
|
10
|
+
regexString += ",";
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
return new RegExp(regexString);
|
|
14
|
+
}
|
|
1
15
|
export function fakeRenderComponent(fun, customHooks = {}) {
|
|
2
16
|
const hooks = window.SP_REACT.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentDispatcher
|
|
3
17
|
.current;
|
|
@@ -42,6 +56,7 @@ export function wrapReactClass(node, prop = 'type') {
|
|
|
42
56
|
const cls = node[prop];
|
|
43
57
|
const wrappedCls = (_a = class extends cls {
|
|
44
58
|
},
|
|
59
|
+
__setFunctionName(_a, "wrappedCls"),
|
|
45
60
|
_a.__DECKY_WRAPPED = true,
|
|
46
61
|
_a);
|
|
47
62
|
return (node[prop] = wrappedCls);
|
package/dist/webpack.js
CHANGED
|
@@ -98,5 +98,5 @@ export const CommonUIModule = modules.find((m) => {
|
|
|
98
98
|
}
|
|
99
99
|
return false;
|
|
100
100
|
});
|
|
101
|
-
export const IconsModule = findModuleByExport((e) => e?.toString && /Spinner\)}\)
|
|
101
|
+
export const IconsModule = findModuleByExport((e) => e?.toString && /Spinner\)}\)?,.\.createElement\(\"path\",{d:\"M18 /.test(e.toString()));
|
|
102
102
|
export const ReactRouter = findModuleByExport((e) => e.computeRootMatch);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decky/ui",
|
|
3
|
-
"version": "4.0
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"description": "A library for interacting with the Steam frontend in Decky plugins and elsewhere.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -41,29 +41,30 @@
|
|
|
41
41
|
}
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
|
-
"@commitlint/cli": "^
|
|
45
|
-
"@commitlint/config-conventional": "^
|
|
46
|
-
"@commitlint/cz-commitlint": "^
|
|
47
|
-
"@semantic-release/changelog": "^6.0.
|
|
44
|
+
"@commitlint/cli": "^19.3.0",
|
|
45
|
+
"@commitlint/config-conventional": "^19.2.2",
|
|
46
|
+
"@commitlint/cz-commitlint": "^19.2.0",
|
|
47
|
+
"@semantic-release/changelog": "^6.0.3",
|
|
48
48
|
"@semantic-release/git": "^10.0.1",
|
|
49
|
-
"@types/jest": "^
|
|
50
|
-
"@types/react": "18.
|
|
51
|
-
"@types/react-
|
|
52
|
-
"
|
|
53
|
-
"
|
|
49
|
+
"@types/jest": "^29.5.12",
|
|
50
|
+
"@types/react": "18.3.3",
|
|
51
|
+
"@types/react-dom": "18.3.0",
|
|
52
|
+
"@types/react-router": "5.1.20",
|
|
53
|
+
"commitizen": "^4.3.0",
|
|
54
|
+
"husky": "^9.0.11",
|
|
54
55
|
"import-sort-style-module": "^6.0.0",
|
|
55
|
-
"jest": "^
|
|
56
|
-
"minimist": "^1.2.
|
|
57
|
-
"prettier": "^
|
|
56
|
+
"jest": "^29.7.0",
|
|
57
|
+
"minimist": "^1.2.8",
|
|
58
|
+
"prettier": "^3.3.2",
|
|
58
59
|
"prettier-plugin-import-sort": "^0.0.7",
|
|
59
|
-
"semantic-release": "^
|
|
60
|
+
"semantic-release": "^24.0.0",
|
|
60
61
|
"shx": "^0.3.4",
|
|
61
|
-
"ts-jest": "^
|
|
62
|
-
"typedoc": "^0.
|
|
63
|
-
"typedoc-plugin-mdn-links": "^
|
|
64
|
-
"typedoc-plugin-missing-exports": "^
|
|
62
|
+
"ts-jest": "^29.1.4",
|
|
63
|
+
"typedoc": "^0.25.13",
|
|
64
|
+
"typedoc-plugin-mdn-links": "^3.1.29",
|
|
65
|
+
"typedoc-plugin-missing-exports": "^2.3.0",
|
|
65
66
|
"typedoc-wikijs-theme": "^1.0.5",
|
|
66
|
-
"typescript": "^4.
|
|
67
|
+
"typescript": "^5.4.5"
|
|
67
68
|
},
|
|
68
69
|
"pnpm": {
|
|
69
70
|
"peerDependencyRules": {
|
|
@@ -71,6 +72,12 @@
|
|
|
71
72
|
"react",
|
|
72
73
|
"react-dom"
|
|
73
74
|
]
|
|
75
|
+
},
|
|
76
|
+
"updateConfig": {
|
|
77
|
+
"ignoreDependencies": [
|
|
78
|
+
"react",
|
|
79
|
+
"react-dom"
|
|
80
|
+
]
|
|
74
81
|
}
|
|
75
82
|
},
|
|
76
83
|
"importSort": {
|
|
@@ -2,13 +2,15 @@ import { FC } from 'react';
|
|
|
2
2
|
|
|
3
3
|
import { CommonUIModule } from '../webpack';
|
|
4
4
|
import { ItemProps } from './Item';
|
|
5
|
+
import { createPropListRegex } from '../utils';
|
|
5
6
|
|
|
6
7
|
export interface ButtonItemProps extends ItemProps {
|
|
7
8
|
onClick?(e: MouseEvent): void;
|
|
8
9
|
disabled?: boolean;
|
|
9
10
|
}
|
|
11
|
+
const buttonItemRegex = createPropListRegex(["highlightOnFocus", "childrenContainerWidth"], false);
|
|
10
12
|
export const ButtonItem = Object.values(CommonUIModule).find(
|
|
11
13
|
(mod: any) =>
|
|
12
|
-
mod?.render?.toString(
|
|
13
|
-
mod?.render?.toString()
|
|
14
|
+
(mod?.render?.toString && buttonItemRegex.test(mod.render.toString())) ||
|
|
15
|
+
mod?.render?.toString?.().includes('childrenContainerWidth:"min"'),
|
|
14
16
|
) as FC<ButtonItemProps>;
|
package/src/components/Dialog.ts
CHANGED
|
@@ -52,13 +52,19 @@ export interface DialogButtonProps extends DialogCommonProps, FooterLegendProps
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
const CommonDialogDivs = Object.values(CommonUIModule).filter(
|
|
55
|
-
(m: any) => typeof m === 'object' && m?.render?.toString().includes('"div",
|
|
55
|
+
(m: any) => typeof m === 'object' && m?.render?.toString().includes('createElement("div",{...') ||
|
|
56
|
+
m?.render?.toString().includes('createElement("div",Object.assign({},'),
|
|
56
57
|
);
|
|
57
58
|
const MappedDialogDivs = new Map(
|
|
58
59
|
Object.values(CommonDialogDivs).map((m: any) => {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
try {
|
|
61
|
+
const renderedDiv = m.render({});
|
|
62
|
+
// Take only the first class name segment as it identifies the element we want
|
|
63
|
+
return [renderedDiv.props.className.split(' ')[0], m];
|
|
64
|
+
} catch (e) {
|
|
65
|
+
console.error("[DFL:Dialog]: failed to render common dialog component", e);
|
|
66
|
+
return [null, null];
|
|
67
|
+
}
|
|
62
68
|
}),
|
|
63
69
|
);
|
|
64
70
|
|
|
@@ -72,14 +78,11 @@ export const DialogControlsSection = MappedDialogDivs.get('DialogControlsSection
|
|
|
72
78
|
export const DialogControlsSectionHeader = MappedDialogDivs.get('DialogControlsSectionHeader') as FC<DialogCommonProps>;
|
|
73
79
|
|
|
74
80
|
export const DialogButtonPrimary = Object.values(CommonUIModule).find(
|
|
75
|
-
(mod: any) => mod?.render?.toString()?.includes('DialogButton
|
|
81
|
+
(mod: any) => mod?.render?.toString()?.includes('"DialogButton","_DialogLayout","Primary"'),
|
|
76
82
|
) as FC<DialogButtonProps>;
|
|
77
83
|
|
|
78
84
|
export const DialogButtonSecondary = Object.values(CommonUIModule).find(
|
|
79
|
-
(mod: any) =>
|
|
80
|
-
mod?.render?.toString()?.includes('Object.assign({type:"button"') &&
|
|
81
|
-
mod?.render?.toString()?.includes('DialogButton') &&
|
|
82
|
-
mod?.render?.toString()?.includes('Secondary'),
|
|
85
|
+
(mod: any) => mod?.render?.toString()?.includes('"DialogButton","_DialogLayout","Secondary"')
|
|
83
86
|
) as FC<DialogButtonProps>;
|
|
84
87
|
|
|
85
88
|
// This is the "main" button. The Primary can act as a submit button,
|
|
@@ -2,6 +2,7 @@ import { ReactNode, FC } from 'react';
|
|
|
2
2
|
|
|
3
3
|
import { CommonUIModule } from '../webpack';
|
|
4
4
|
import { ItemProps } from './Item';
|
|
5
|
+
import { createPropListRegex } from '../utils';
|
|
5
6
|
|
|
6
7
|
export interface SingleDropdownOption {
|
|
7
8
|
data: any;
|
|
@@ -44,6 +45,7 @@ export const Dropdown = Object.values(CommonUIModule).find(
|
|
|
44
45
|
|
|
45
46
|
export interface DropdownItemProps extends DropdownProps, ItemProps {}
|
|
46
47
|
|
|
48
|
+
const dropdownItemRegex = createPropListRegex(["dropDownControlRef", "description"], false);
|
|
47
49
|
export const DropdownItem = Object.values(CommonUIModule).find((mod: any) =>
|
|
48
|
-
mod?.toString(
|
|
50
|
+
mod?.toString && dropdownItemRegex.test(mod.toString()),
|
|
49
51
|
) as FC<DropdownItemProps>;
|
|
@@ -2,6 +2,7 @@ import { HTMLAttributes, ReactNode, RefAttributes, FC } from 'react';
|
|
|
2
2
|
|
|
3
3
|
import { Export, findModuleExport } from '../webpack';
|
|
4
4
|
import { FooterLegendProps } from './FooterLegend';
|
|
5
|
+
import { createPropListRegex } from '../utils';
|
|
5
6
|
|
|
6
7
|
export interface FocusableProps extends HTMLAttributes<HTMLDivElement>, FooterLegendProps {
|
|
7
8
|
children: ReactNode;
|
|
@@ -13,6 +14,8 @@ export interface FocusableProps extends HTMLAttributes<HTMLDivElement>, FooterLe
|
|
|
13
14
|
onCancel?: (e: CustomEvent) => void;
|
|
14
15
|
}
|
|
15
16
|
|
|
17
|
+
const focusableRegex = createPropListRegex(["flow-children", "onActivate", "onCancel", "focusClassName", "focusWithinClassName"]);
|
|
18
|
+
|
|
16
19
|
export const Focusable = findModuleExport((e: Export) =>
|
|
17
|
-
e?.render?.toString(
|
|
20
|
+
e?.render?.toString && focusableRegex.test(e.render.toString())
|
|
18
21
|
) as FC<FocusableProps & RefAttributes<HTMLDivElement>>;
|
package/src/components/Menu.ts
CHANGED
|
@@ -5,7 +5,8 @@ import { Export, findModuleExport } from '../webpack';
|
|
|
5
5
|
import { FooterLegendProps } from './FooterLegend';
|
|
6
6
|
|
|
7
7
|
export const showContextMenu: (children: ReactNode, parent?: EventTarget) => void = findModuleExport(
|
|
8
|
-
(e: Export) => typeof e === 'function' && e.toString().includes('
|
|
8
|
+
(e: Export) => typeof e === 'function' && e.toString().includes('GetContextMenuManagerFromWindow(')
|
|
9
|
+
&& e.toString().includes('.CreateContextMenuInstance('),
|
|
9
10
|
);
|
|
10
11
|
|
|
11
12
|
export interface MenuProps extends FooterLegendProps {
|
|
@@ -28,8 +29,8 @@ export interface MenuGroupProps {
|
|
|
28
29
|
export const MenuGroup: FC<MenuGroupProps> = findModuleExport(
|
|
29
30
|
(e: Export) =>
|
|
30
31
|
(e?.toString()?.includes?.('bInGamepadUI:') &&
|
|
31
|
-
fakeRenderComponent(() => e({ overview: { appid: 7 } }))?.type?.prototype?.RenderSubMenu) ||
|
|
32
|
-
(e?.prototype?.RenderSubMenu && e?.prototype?.ShowSubMenu)
|
|
32
|
+
fakeRenderComponent(() => e({ overview: { appid: 7 } }), {useContext: () => ({IN_GAMEPADUI: true})})?.type?.prototype?.RenderSubMenu) ||
|
|
33
|
+
(e?.prototype?.RenderSubMenu && e?.prototype?.ShowSubMenu)
|
|
33
34
|
);
|
|
34
35
|
export interface MenuItemProps extends FooterLegendProps {
|
|
35
36
|
bInteractableItem?: boolean;
|
|
@@ -2,6 +2,7 @@ import { ReactNode, FC } from 'react';
|
|
|
2
2
|
|
|
3
3
|
import { Export, findModuleExport } from '../webpack';
|
|
4
4
|
import { ItemProps } from './Item';
|
|
5
|
+
import { createPropListRegex } from '../utils';
|
|
5
6
|
|
|
6
7
|
export interface ProgressBarItemProps extends ItemProps {
|
|
7
8
|
indeterminate?: boolean;
|
|
@@ -30,6 +31,7 @@ export const ProgressBarWithInfo = findModuleExport((e: Export) =>
|
|
|
30
31
|
e?.toString()?.includes('.ProgressBarFieldStatus},'),
|
|
31
32
|
) as FC<ProgressBarWithInfoProps>;
|
|
32
33
|
|
|
34
|
+
const progressBarItemRegex = createPropListRegex(["indeterminate", "nTransitionSec", "nProgress"]);
|
|
33
35
|
export const ProgressBarItem = findModuleExport((e: Export) =>
|
|
34
|
-
e?.toString(
|
|
36
|
+
e?.toString && progressBarItemRegex.test(e.toString()),
|
|
35
37
|
) as FC<ProgressBarItemProps>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ReactNode, FC } from 'react';
|
|
2
2
|
|
|
3
3
|
import { Export, findModuleExport } from '../webpack';
|
|
4
|
+
import { createPropListRegex } from '../utils';
|
|
4
5
|
|
|
5
6
|
export interface SidebarNavigationPage {
|
|
6
7
|
title: ReactNode;
|
|
@@ -23,6 +24,7 @@ export interface SidebarNavigationProps {
|
|
|
23
24
|
onPageRequested?: (page: string) => void;
|
|
24
25
|
}
|
|
25
26
|
|
|
27
|
+
const sidebarNavigationRegex = createPropListRegex(["pages", "fnSetNavigateToPage", "disableRouteReporting"]);
|
|
26
28
|
export const SidebarNavigation = findModuleExport((e: Export) =>
|
|
27
|
-
e?.toString(
|
|
29
|
+
e?.toString && sidebarNavigationRegex.test(e.toString()),
|
|
28
30
|
) as FC<SidebarNavigationProps>;
|
|
@@ -3,6 +3,6 @@ import { FC, SVGAttributes } from 'react';
|
|
|
3
3
|
import { IconsModule } from '../webpack';
|
|
4
4
|
|
|
5
5
|
// TODO type this and other icons?
|
|
6
|
-
export const Spinner = Object.values(IconsModule)
|
|
7
|
-
(mod: any) => mod?.toString && /Spinner\)}\)
|
|
6
|
+
export const Spinner = Object.values(IconsModule)?.find(
|
|
7
|
+
(mod: any) => mod?.toString && /Spinner\)}\)?,.\.createElement\(\"path\",{d:\"M18 /.test(mod.toString()),
|
|
8
8
|
) as FC<SVGAttributes<SVGElement>>;
|
package/src/index.ts
CHANGED
|
@@ -13,8 +13,8 @@ export * from './class-mapper';
|
|
|
13
13
|
* @deprecated use @decky/api instead
|
|
14
14
|
*/
|
|
15
15
|
export const definePlugin = (fn: any): any => {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
return (...args: any[]) => {
|
|
17
|
+
// TODO: Maybe wrap this
|
|
18
|
+
return fn(...args);
|
|
19
|
+
};
|
|
20
20
|
};
|
package/src/utils/react.ts
CHANGED
|
@@ -8,6 +8,28 @@ declare global {
|
|
|
8
8
|
}
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Create a Regular Expression to search for a React component that uses certain props in order.
|
|
13
|
+
*
|
|
14
|
+
* @export
|
|
15
|
+
* @param {string[]} propList Ordererd list of properties to search for
|
|
16
|
+
* @returns {RegExp} RegEx to call .test(component.toString()) on
|
|
17
|
+
*/
|
|
18
|
+
export function createPropListRegex(propList: string[], fromStart: boolean = true): RegExp {
|
|
19
|
+
let regexString = fromStart ? "const\{" : "";
|
|
20
|
+
propList.forEach((prop: any, propIdx) => {
|
|
21
|
+
regexString += `"?${prop}"?:[a-zA-Z_$]{1,2}`;
|
|
22
|
+
if (propIdx < propList.length - 1) {
|
|
23
|
+
regexString += ",";
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// TODO provide a way to enable this
|
|
28
|
+
// console.debug(`[DFL:Utils] createPropListRegex generated regex "${regexString}" for props`, propList);
|
|
29
|
+
|
|
30
|
+
return new RegExp(regexString);
|
|
31
|
+
}
|
|
32
|
+
|
|
11
33
|
export function fakeRenderComponent(fun: Function, customHooks: any = {}): any {
|
|
12
34
|
const hooks = (window.SP_REACT as any).__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentDispatcher
|
|
13
35
|
.current;
|
package/src/webpack.ts
CHANGED
|
@@ -130,7 +130,7 @@ export const CommonUIModule = modules.find((m: Module) => {
|
|
|
130
130
|
});
|
|
131
131
|
|
|
132
132
|
export const IconsModule = findModuleByExport(
|
|
133
|
-
(e) => e?.toString && /Spinner\)}\)
|
|
133
|
+
(e) => e?.toString && /Spinner\)}\)?,.\.createElement\(\"path\",{d:\"M18 /.test(e.toString()),
|
|
134
134
|
);
|
|
135
135
|
|
|
136
136
|
export const ReactRouter = findModuleByExport((e) => e.computeRootMatch);
|