Compare commits

...

13 Commits

Author SHA1 Message Date
semantic-release-bot
b151bdce8b chore(release): 4.11.4 [CI SKIP] 2026-05-15 03:58:28 +00:00
AAGaming
e44664c970 fix(dropdown): hack to fix styling in dropdownitem 2026-05-14 23:57:56 -04:00
AAGaming
ac8da8e9b6 fix(Field): fix filter for field on beta 2026-05-14 23:57:06 -04:00
semantic-release-bot
b688bc3544 chore(release): 4.11.3 [CI SKIP] 2026-03-22 00:34:53 +00:00
Jozen Blue Martinez
3126dd3e04 fix(Tabs): update for latest beta (#129) 2026-03-21 20:34:27 -04:00
semantic-release-bot
8596294667 chore(release): 4.11.2 [CI SKIP] 2026-03-21 01:29:42 +00:00
AAGaming
772a85523b fix(ci): update semantic release 2026-03-20 21:29:12 -04:00
AAGaming
aabc522740 fix(ci): microsoft 2026-03-20 21:26:44 -04:00
AAGaming
66e0afccae fix(ci): microsoft stinks 2026-03-20 21:23:44 -04:00
AAGaming
77e6acd828 fix(ci): use new npm publishing auth 2026-03-20 21:22:23 -04:00
AAGaming
261162c8bc fix(components): update for latest beta 2026-03-20 20:27:59 -04:00
semantic-release-bot
f8fda380f1 chore(release): 4.11.1 [CI SKIP] 2025-11-27 02:56:17 +00:00
AAGaming
7242c69758 fix(webpack): ignore window module (lol), ignore filter errors 2025-11-26 21:55:32 -05:00
15 changed files with 647 additions and 369 deletions

View File

@@ -6,6 +6,10 @@ on:
branches:
- main
permissions:
id-token: write
contents: write
jobs:
release:
name: Release
@@ -18,7 +22,7 @@ jobs:
- name: Setup | Node.js
uses: actions/setup-node@v4
with:
node-version: 20
node-version: 24
- name: Setup | Dependencies
run: npm i -g pnpm && pnpm i --frozen-lockfile
- name: Build
@@ -29,5 +33,4 @@ jobs:
if: github.event_name != 'pull_request'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN_DECKY_ORG }}
run: pnpm exec semantic-release

View File

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

View File

@@ -1,3 +1,36 @@
## [4.11.4](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v4.11.3...v4.11.4) (2026-05-15)
### Bug Fixes
* **dropdown:** hack to fix styling in dropdownitem ([e44664c](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/e44664c9704b3b284284619bc26cf6a910890136))
* **Field:** fix filter for field on beta ([ac8da8e](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/ac8da8e9b650cfcabcd3e5752e1475e91e7edf7a))
## [4.11.3](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v4.11.2...v4.11.3) (2026-03-22)
### Bug Fixes
* **Tabs:** update for latest beta ([#129](https://github.com/SteamDeckHomebrew/decky-frontend-lib/issues/129)) ([3126dd3](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/3126dd3e040eaef00eb0362b69efb143d7e01030))
## [4.11.2](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v4.11.1...v4.11.2) (2026-03-21)
### Bug Fixes
* **ci:** microsoft ([aabc522](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/aabc5227400f1660ddcb7917fa8f305ad2fce3d7))
* **ci:** microsoft stinks ([66e0afc](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/66e0afccae6803ee57d817621340d09a12ba75d8))
* **ci:** update semantic release ([772a855](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/772a85523b8c4bbf80ccd4827a89801e3a51cfd6))
* **ci:** use new npm publishing auth ([77e6acd](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/77e6acd828b1d0f0978356292b1c46bfc3b8e422))
* **components:** update for latest beta ([261162c](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/261162c8bceecc50afad07d57a78b85b98936fe1))
## [4.11.1](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v4.11.0...v4.11.1) (2025-11-27)
### Bug Fixes
* **webpack:** ignore window module (lol), ignore filter errors ([7242c69](https://github.com/SteamDeckHomebrew/decky-frontend-lib/commit/7242c697580f3a6a6d373eaacc4fb83ccff9bd2a))
# [4.11.0](https://github.com/SteamDeckHomebrew/decky-frontend-lib/compare/v4.10.6...v4.11.0) (2025-10-15)

View File

@@ -1,6 +1,6 @@
{
"name": "@decky/ui",
"version": "4.11.0",
"version": "4.11.4",
"description": "A library for interacting with the Steam frontend in Decky plugins and elsewhere.",
"main": "dist/index.js",
"types": "dist/index.d.ts",
@@ -57,7 +57,7 @@
"minimist": "^1.2.8",
"prettier": "^3.6.2",
"prettier-plugin-import-sort": "^0.0.7",
"semantic-release": "^24.2.7",
"semantic-release": "^25.0.3",
"shx": "^0.3.4",
"ts-jest": "^29.4.1",
"typedoc": "^0.25.13",

894
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -52,8 +52,17 @@ export interface DialogButtonProps extends DialogCommonProps, FooterLegendProps
}
const CommonDialogDivs = Object.values(CommonUIModule).filter(
(m: any) => typeof m === 'object' && m?.render?.toString().includes('createElement("div",{...') ||
m?.render?.toString().includes('createElement("div",Object.assign({},'),
(m: any) => typeof m === 'object' &&
// New
(
m?.render?.toString().includes('jsx)("div",{...') ||
m?.render?.toString().includes('jsx)("div",Object.assign({},')
) ||
// Old
(
m?.render?.toString().includes('createElement("div",{...') ||
m?.render?.toString().includes('createElement("div",Object.assign({},')
)
);
const MappedDialogDivs = new Map(
Object.values(CommonDialogDivs).map((m: any) => {

View File

@@ -42,6 +42,8 @@ export const Dropdown = Object.values(CommonUIModule).find(
export interface DropdownItemProps extends DropdownProps, ItemProps {}
const dropdownItemRegex = createPropListRegex(["dropDownControlRef", "description"], false);
export const DropdownItem = Object.values(CommonUIModule).find((mod: any) =>
export const DropdownItemInternal = Object.values(CommonUIModule).find((mod: any) =>
mod?.toString && dropdownItemRegex.test(mod.toString()),
) as FC<DropdownItemProps>;
export const DropdownItem = ((args: DropdownItemProps) => <DropdownItemInternal childrenContainerWidth="min" {...args}/>) as FC<DropdownItemProps>;

View File

@@ -24,6 +24,7 @@ export interface FieldProps extends FooterLegendProps {
onClick?: (e: CustomEvent | MouseEvent) => void;
}
export const Field = findModuleExport((e: Export) => e?.render?.toString().includes('"shift-children-below"')) as FC<
// new || old
export const Field = findModuleExport((e: Export) => (e?.toString()?.includes('().Field') && e?.toString()?.includes('"shift-children-below"')) || e?.render?.toString()?.includes('"shift-children-below"')) as FC<
FieldProps & RefAttributes<HTMLDivElement>
>;

View File

@@ -7,6 +7,7 @@ export interface ItemProps {
layout?: 'below' | 'inline';
icon?: ReactNode;
bottomSeparator?: 'standard' | 'thick' | 'none';
childrenContainerWidth?: 'min' | 'max' | 'fixed'; // Does not work with layout==='below'
indentLevel?: number;
tooltip?: string;
highlightOnFocus?: boolean;

View File

@@ -28,7 +28,8 @@ export const ProgressBar = findModuleExport((e: Export) =>
) as FC<ProgressBarProps>;
export const ProgressBarWithInfo = findModuleExport((e: Export) =>
e?.toString?.()?.includes('.ProgressBarFieldStatus},'),
// new || old
e?.toString?.()?.includes('.ProgressBarFieldStatus,children') || e?.toString?.()?.includes('.ProgressBarFieldStatus},'),
) as FC<ProgressBarWithInfoProps>;
const progressBarItemRegex = createPropListRegex(["indeterminate", "nTransitionSec", "nProgress"]);

View File

@@ -11,5 +11,6 @@ export const ScrollPanel = ScrollingModuleProps.find((prop: any) =>
) as FC<{ children?: ReactNode }>;
export const ScrollPanelGroup: FC<{ children?: ReactNode }> = findModuleExport((e: Export) =>
e?.render?.toString().includes('.FocusVisibleChild()),[])'),
// new || old
e?.render?.toString().includes('.FocusVisibleChild(),[])') || e?.render?.toString().includes('.FocusVisibleChild()),[])'),
);

View File

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

View File

@@ -67,4 +67,4 @@ const tabsModule = findModuleByExport(e => e?.toString?.()?.includes(".TabRowTab
/**
* Tabs component as used in the library and media tabs. See {@link TabsProps}.
*/
export const Tabs = tabsModule && Object.values(tabsModule).find((e: any) => e?.type?.toString?.()?.includes("((function()")) as FC<TabsProps>;
export const Tabs = tabsModule && Object.values(tabsModule).find((e: any) => e?.type?.toString?.()?.includes("(function()")) as FC<TabsProps>;

View File

@@ -42,6 +42,9 @@ export function injectFCTrampoline(component: FC, customHooks?: any): FCTrampoli
};
component.prototype.isReactComponent = true;
let stubsApplied = false;
const patchJsx = window.SP_REACTDOM.version.startsWith("19.");
let oldJsx = window.SP_JSX?.jsx;
let oldJsxs = window.SP_JSX?.jsxs;
let oldCreateElement = window.SP_REACT.createElement;
const applyStubsIfNeeded = () => {
@@ -55,6 +58,18 @@ export function injectFCTrampoline(component: FC, customHooks?: any): FCTrampoli
loggingEnabled && console.trace("createElement trace");
return Object.create(component.prototype);
};
if (patchJsx) {
window.SP_JSX.jsx = () => {
loggingEnabled && logger.debug("jsx hook called");
loggingEnabled && console.trace("jsx trace");
return Object.create(component.prototype);
}
window.SP_JSX.jsxs = () => {
loggingEnabled && logger.debug("jsxs hook called");
loggingEnabled && console.trace("jsxs trace");
return Object.create(component.prototype);
}
}
}
}
@@ -64,6 +79,10 @@ export function injectFCTrampoline(component: FC, customHooks?: any): FCTrampoli
stubsApplied = false;
removeHookStubs();
window.SP_REACT.createElement = oldCreateElement;
if (patchJsx) {
window.SP_JSX.jsx = oldJsx;
window.SP_JSX.jsxs = oldJsxs;
}
}
}

View File

@@ -21,7 +21,7 @@ export let modules = new Map<ModuleID, Module>();
function initModuleCache() {
const startTime = performance.now();
logger.group('Webpack Module Init');
// Webpack 5, currently on beta
// Webpack 5
// Generate a fake module ID
const id = Symbol("@decky/ui");
let webpackRequire!: ((id: any) => Module) & { m: object };
@@ -70,15 +70,22 @@ export const findModuleDetailsByExport = (
for (const [id, m] of modules) {
if (!m) continue;
for (const mod of [m.default, m]) {
// special cases
if (typeof mod !== 'object') continue;
if (mod == window) continue; // wtf
if (minExports && Object.keys(mod).length < minExports) continue;
for (let exportName in mod) {
if (mod?.[exportName]) {
const filterRes = filter(mod[exportName], exportName);
if (filterRes) {
return [mod, mod[exportName], exportName, id];
} else {
continue;
try {
const filterRes = filter(mod[exportName], exportName);
if (filterRes) {
return [mod, mod[exportName], exportName, id];
} else {
continue;
}
} catch (e) {
logger.warn("Webpack filter threw exception: ", e);
}
}
}
@@ -99,6 +106,7 @@ export const findModuleExport = (filter: ExportFilterFn, minExports?: number) =>
* @deprecated use findModuleExport instead
*/
export const findModuleChild = (filter: FindFn) => {
logger.warn("findModuleChild is deprecated and will be removed soon. Use findModuleExport instead. Used in:", new Error().stack?.substring(5))
for (const m of modules.values()) {
for (const mod of [m.default, m]) {
const filterRes = filter(mod);
@@ -115,6 +123,7 @@ export const findModuleChild = (filter: FindFn) => {
* @deprecated use createModuleMapping instead
*/
export const findAllModules = (filter: FilterFn) => {
logger.warn("findAllModules is deprecated and will be removed soon. Use createModuleMapping instead. Used in:", new Error().stack?.substring(5))
const out = [];
for (const m of modules.values()) {
@@ -145,7 +154,7 @@ export const CommonUIModule = findModule((m: Module) => {
});
export const IconsModule = findModuleByExport(
(e) => e?.toString && /Spinner\)}\)?,.\.createElement\(\"path\",{d:\"M18 /.test(e.toString()),
(e) => e?.toString && /Spinner\),children:\[\(0,\w+\.jsx\)\("path",\{d:"M18 /.test(e.toString()) || /Spinner\)}\)?,.\.createElement\(\"path\",{d:\"M18 /.test(e.toString()),
);
export const ReactRouter = findModuleByExport((e) => e.computeRootMatch);