summaryrefslogtreecommitdiff
path: root/frontend/src/components
diff options
context:
space:
mode:
authorAAGaming <aagaming@riseup.net>2026-10-26 23:50:59 -0400
committerAAGaming <aagaming@riseup.net>2026-10-26 23:50:59 -0400
commite82a55e9582fbd6d84f3f4510b04480c35e41fe4 (patch)
tree1fe05c2296bbedbf301137a67a28ef542804fd69 /frontend/src/components
parent6301508037b30489c42ee9ea8acee97cdea76297 (diff)
downloaddecky-loader-e82a55e9582fbd6d84f3f4510b04480c35e41fe4.tar.gz
decky-loader-e82a55e9582fbd6d84f3f4510b04480c35e41fe4.zip
feat: initial VERY VERY incomplete steam frame support
Diffstat (limited to 'frontend/src/components')
-rw-r--r--frontend/src/components/NotificationBadge.tsx55
-rw-r--r--frontend/src/components/PluginView.tsx40
-rw-r--r--frontend/src/components/QuickAccessVisibleState.tsx17
-rw-r--r--frontend/src/components/settings/pages/developer/index.tsx33
4 files changed, 98 insertions, 47 deletions
diff --git a/frontend/src/components/NotificationBadge.tsx b/frontend/src/components/NotificationBadge.tsx
index 3c5d8215..1d14d576 100644
--- a/frontend/src/components/NotificationBadge.tsx
+++ b/frontend/src/components/NotificationBadge.tsx
@@ -2,23 +2,52 @@ import { CSSProperties, FunctionComponent } from 'react';
interface NotificationBadgeProps {
show?: boolean;
+ vr?: boolean;
style?: CSSProperties;
}
-const NotificationBadge: FunctionComponent<NotificationBadgeProps> = ({ show, style }) => {
+const NotificationBadge: FunctionComponent<NotificationBadgeProps> = ({ show, style, vr }) => {
return show ? (
- <div
- style={{
- position: 'absolute',
- top: '8px',
- right: '8px',
- height: '10px',
- width: '10px',
- background: 'orange',
- borderRadius: '50%',
- ...style,
- }}
- />
+ vr ? (
+ // whoever made transform:scale completely break absolute positioning goes straight to super turbo hell this took me an hour
+ <div
+ style={{
+ top: 0,
+ left: 0,
+ marginLeft: 'auto',
+ width: 0,
+ height: 0,
+ overflow: 'visible',
+ transform: 'translate(0px, 0px)',
+ }}
+ >
+ <div
+ style={{
+ position: 'fixed',
+ top: '-18px',
+ right: '-6px',
+ width: '10px',
+ height: '10px',
+ background: 'orange',
+ borderRadius: '50%',
+ ...style,
+ }}
+ />
+ </div>
+ ) : (
+ <div
+ style={{
+ position: 'absolute',
+ top: '8px',
+ right: '8px',
+ height: '10px',
+ width: '10px',
+ background: 'orange',
+ borderRadius: '50%',
+ ...style,
+ }}
+ />
+ )
) : null;
};
diff --git a/frontend/src/components/PluginView.tsx b/frontend/src/components/PluginView.tsx
index fbafaf05..27c1f2f1 100644
--- a/frontend/src/components/PluginView.tsx
+++ b/frontend/src/components/PluginView.tsx
@@ -1,4 +1,12 @@
-import { ButtonItem, ErrorBoundary, Focusable, PanelSection, PanelSectionRow } from '@decky/ui';
+import {
+ ButtonItem,
+ ErrorBoundary,
+ Focusable,
+ PanelSection,
+ PanelSectionRow,
+ joinClassNames,
+ vrDashboardClasses,
+} from '@decky/ui';
import { FC, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { FaBan, FaEyeSlash } from 'react-icons/fa';
@@ -8,7 +16,7 @@ import NotificationBadge from './NotificationBadge';
import { useQuickAccessVisible } from './QuickAccessVisibleState';
import TitleView from './TitleView';
-const PluginView: FC = () => {
+const PluginView: FC<{ vr?: boolean }> = ({ vr }) => {
const {
plugins,
disabledPlugins,
@@ -40,20 +48,38 @@ const PluginView: FC = () => {
if (activePlugin) {
return (
- <Focusable onCancelButton={closeActivePlugin}>
+ <Focusable
+ onCancelButton={closeActivePlugin}
+ className={
+ (vr && joinClassNames(vrDashboardClasses.QuickAccessPopupPanel, vrDashboardClasses.BottomFade)) || ''
+ }
+ style={{
+ display: 'flex',
+ flexDirection: 'column',
+ paddingBottom: '20px', // override bottomfade, there is prolly a better way
+ }}
+ >
<TitleView />
- <div style={{ height: '100%', paddingTop: '16px' }}>
+ <div style={{ paddingTop: '16px', overflowY: vr ? 'auto' : undefined }}>
<ErrorBoundary>{(visible || activePlugin.alwaysRender) && activePlugin.content}</ErrorBoundary>
</div>
</Focusable>
);
}
return (
- <>
+ <div
+ className={(vr && joinClassNames(vrDashboardClasses.QuickAccessPopupPanel, vrDashboardClasses.BottomFade)) || ''}
+ style={{
+ display: 'flex',
+ flexDirection: 'column',
+ paddingBottom: '20px', // override bottomfade, there is prolly a better way
+ }}
+ >
<TitleView />
<div
style={{
- paddingTop: '16px',
+ paddingTop: vr ? '16px' : '16px',
+ overflowY: vr ? 'auto' : undefined,
}}
>
<PanelSection>
@@ -92,7 +118,7 @@ const PluginView: FC = () => {
</div>
</PanelSection>
</div>
- </>
+ </div>
);
};
diff --git a/frontend/src/components/QuickAccessVisibleState.tsx b/frontend/src/components/QuickAccessVisibleState.tsx
index f90c24b4..83e45177 100644
--- a/frontend/src/components/QuickAccessVisibleState.tsx
+++ b/frontend/src/components/QuickAccessVisibleState.tsx
@@ -1,16 +1,21 @@
import { FC, PropsWithChildren, createContext, useContext, useState } from 'react';
-const QuickAccessVisibleState = createContext<boolean>(false);
+export const QuickAccessVisibleState = createContext<boolean>(false);
export const useQuickAccessVisible = () => useContext(QuickAccessVisibleState);
-export const QuickAccessVisibleStateProvider: FC<PropsWithChildren<{ tab: any }>> = ({ children, tab }) => {
- const initial = tab.initialVisibility;
+export const QuickAccessVisibleStateProvider: FC<PropsWithChildren<{ tab?: any; initialVal?: boolean }>> = ({
+ children,
+ tab,
+ initialVal,
+}) => {
+ const initial = initialVal || tab?.initialVisibility;
const [visible, setVisible] = useState<boolean>(initial);
// HACK but i can't think of a better way to do this
- tab.qAMVisibilitySetter = (val: boolean) => {
- if (val != visible) setVisible(val);
- };
+ if (tab)
+ tab.qAMVisibilitySetter = (val: boolean) => {
+ if (val != visible) setVisible(val);
+ };
return <QuickAccessVisibleState.Provider value={visible}>{children}</QuickAccessVisibleState.Provider>;
};
diff --git a/frontend/src/components/settings/pages/developer/index.tsx b/frontend/src/components/settings/pages/developer/index.tsx
index 17187675..b6a337f0 100644
--- a/frontend/src/components/settings/pages/developer/index.tsx
+++ b/frontend/src/components/settings/pages/developer/index.tsx
@@ -9,7 +9,7 @@ import {
Toggle,
showModal,
} from '@decky/ui';
-import { useRef, useState } from 'react';
+import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { FaFileArchive, FaLink, FaReact, FaSteamSymbol, FaTerminal } from 'react-icons/fa';
@@ -49,7 +49,6 @@ export default function DeveloperSettings() {
const [acceptedWarning, setAcceptedWarning] = useSetting<boolean>('developer.warn.third_party', false);
const waitTime = acceptedWarning ? 0 : 5;
const [pluginURL, setPluginURL] = useState('');
- const textRef = useRef<HTMLDivElement>(null);
const { t } = useTranslation();
return (
@@ -82,13 +81,6 @@ export default function DeveloperSettings() {
</Field>
<Field
label={t('SettingsDeveloperIndex.third_party_plugins.label_url')}
- description={
- <TextField
- label={t('SettingsDeveloperIndex.third_party_plugins.label_desc')}
- value={pluginURL}
- onChange={(e) => setPluginURL(e?.target.value)}
- />
- }
icon={<FaLink style={{ display: 'block' }} />}
>
<DialogButton
@@ -114,6 +106,11 @@ export default function DeveloperSettings() {
{t('SettingsDeveloperIndex.third_party_plugins.button_install')}
</DialogButton>
</Field>
+ <TextField
+ label={t('SettingsDeveloperIndex.third_party_plugins.label_desc')}
+ value={pluginURL}
+ onChange={(e) => setPluginURL(e?.target.value)}
+ />
</DialogControlsSection>
<DialogControlsSection>
<DialogControlsSectionHeader>{t('SettingsDeveloperIndex.header')}</DialogControlsSectionHeader>
@@ -160,18 +157,7 @@ export default function DeveloperSettings() {
<Field
label={t('SettingsDeveloperIndex.react_devtools.label')}
description={
- <>
- <span style={{ whiteSpace: 'pre-line' }}>{t('SettingsDeveloperIndex.react_devtools.desc')}</span>
- <br />
- <br />
- <div ref={textRef}>
- <TextField
- label={t('SettingsDeveloperIndex.react_devtools.ip_label')}
- value={reactDevtoolsIP}
- onChange={(e) => setReactDevtoolsIP(e?.target.value)}
- />
- </div>
- </>
+ <span style={{ whiteSpace: 'pre-line' }}>{t('SettingsDeveloperIndex.react_devtools.desc')}</span>
}
icon={<FaReact style={{ display: 'block' }} />}
>
@@ -184,6 +170,11 @@ export default function DeveloperSettings() {
}}
/>
</Field>
+ <TextField
+ label={t('SettingsDeveloperIndex.react_devtools.ip_label')}
+ value={reactDevtoolsIP}
+ onChange={(e) => setReactDevtoolsIP(e?.target.value)}
+ />
</DialogControlsSection>
</DialogBody>
);