summaryrefslogtreecommitdiff
path: root/frontend/src/components
diff options
context:
space:
mode:
Diffstat (limited to 'frontend/src/components')
-rw-r--r--frontend/src/components/Markdown.tsx39
-rw-r--r--frontend/src/components/PluginView.tsx4
-rw-r--r--frontend/src/components/WithSuspense.tsx38
-rw-r--r--frontend/src/components/modals/PluginInstallModal.tsx7
-rw-r--r--frontend/src/components/modals/filepicker/iconCustomizations.ts170
-rw-r--r--frontend/src/components/modals/filepicker/index.tsx160
-rw-r--r--frontend/src/components/modals/filepicker/patches/README.md1
-rw-r--r--frontend/src/components/modals/filepicker/patches/index.ts10
-rw-r--r--frontend/src/components/modals/filepicker/patches/library.ts57
-rw-r--r--frontend/src/components/settings/pages/general/BranchSelect.tsx5
-rw-r--r--frontend/src/components/settings/pages/general/Updater.tsx86
-rw-r--r--frontend/src/components/store/PluginCard.tsx18
-rw-r--r--frontend/src/components/store/Store.tsx7
13 files changed, 550 insertions, 52 deletions
diff --git a/frontend/src/components/Markdown.tsx b/frontend/src/components/Markdown.tsx
index 7b187f14..278e49cd 100644
--- a/frontend/src/components/Markdown.tsx
+++ b/frontend/src/components/Markdown.tsx
@@ -1,9 +1,42 @@
-import { FunctionComponent } from 'react';
+import { Focusable } from 'decky-frontend-lib';
+import { FunctionComponent, useRef } from 'react';
import ReactMarkdown, { Options as ReactMarkdownOptions } from 'react-markdown';
import remarkGfm from 'remark-gfm';
-const Markdown: FunctionComponent<ReactMarkdownOptions> = (props) => {
- return <ReactMarkdown remarkPlugins={[remarkGfm]} {...props} />;
+interface MarkdownProps extends ReactMarkdownOptions {
+ onDismiss?: () => void;
+}
+
+const Markdown: FunctionComponent<MarkdownProps> = (props) => {
+ return (
+ <Focusable>
+ <ReactMarkdown
+ remarkPlugins={[remarkGfm]}
+ components={{
+ div: (nodeProps) => <Focusable {...nodeProps.node.properties}>{nodeProps.children}</Focusable>,
+ a: (nodeProps) => {
+ const aRef = useRef<HTMLAnchorElement>(null);
+ return (
+ // TODO fix focus ring
+ <Focusable
+ onActivate={() => {}}
+ onOKButton={() => {
+ aRef?.current?.click();
+ props.onDismiss?.();
+ }}
+ style={{ display: 'inline' }}
+ >
+ <a ref={aRef} {...nodeProps.node.properties}>
+ {nodeProps.children}
+ </a>
+ </Focusable>
+ );
+ },
+ }}
+ {...props}
+ />
+ </Focusable>
+ );
};
export default Markdown;
diff --git a/frontend/src/components/PluginView.tsx b/frontend/src/components/PluginView.tsx
index 67a203c9..0d0a52cf 100644
--- a/frontend/src/components/PluginView.tsx
+++ b/frontend/src/components/PluginView.tsx
@@ -32,8 +32,8 @@ const PluginView: VFC = () => {
.map(({ name, icon }) => (
<PanelSectionRow key={name}>
<ButtonItem layout="below" onClick={() => setActivePlugin(name)}>
- <div style={{ display: 'flex', justifyContent: 'space-between' }}>
- <div>{icon}</div>
+ <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
+ {icon}
<div>{name}</div>
<NotificationBadge show={updates?.has(name)} style={{ top: '-5px', right: '-5px' }} />
</div>
diff --git a/frontend/src/components/WithSuspense.tsx b/frontend/src/components/WithSuspense.tsx
new file mode 100644
index 00000000..402f5e5b
--- /dev/null
+++ b/frontend/src/components/WithSuspense.tsx
@@ -0,0 +1,38 @@
+import { Focusable, SteamSpinner } from 'decky-frontend-lib';
+import { FunctionComponent, ReactElement, ReactNode, Suspense } from 'react';
+
+interface WithSuspenseProps {
+ children: ReactNode;
+ route?: boolean;
+}
+
+// Nice little wrapper around Suspense so we don't have to duplicate the styles and code for the loading spinner
+const WithSuspense: FunctionComponent<WithSuspenseProps> = (props) => {
+ const propsCopy = { ...props };
+ delete propsCopy.children;
+ (props.children as ReactElement)?.props && Object.assign((props.children as ReactElement).props, propsCopy); // There is probably a better way to do this but valve does it this way so ¯\_(ツ)_/¯
+ return (
+ <Suspense
+ fallback={
+ <Focusable
+ // needed to enable focus ring so that the focus properly resets on load
+ onActivate={() => {}}
+ style={{
+ overflowY: 'scroll',
+ backgroundColor: 'transparent',
+ ...(props.route && {
+ marginTop: '40px',
+ height: 'calc( 100% - 40px )',
+ }),
+ }}
+ >
+ <SteamSpinner />
+ </Focusable>
+ }
+ >
+ {props.children}
+ </Suspense>
+ );
+};
+
+export default WithSuspense;
diff --git a/frontend/src/components/modals/PluginInstallModal.tsx b/frontend/src/components/modals/PluginInstallModal.tsx
index 2c0c0bba..8b927523 100644
--- a/frontend/src/components/modals/PluginInstallModal.tsx
+++ b/frontend/src/components/modals/PluginInstallModal.tsx
@@ -1,4 +1,4 @@
-import { ModalRoot, QuickAccessTab, Router, Spinner, staticClasses } from 'decky-frontend-lib';
+import { ConfirmModal, QuickAccessTab, Router, Spinner, staticClasses } from 'decky-frontend-lib';
import { FC, useState } from 'react';
interface PluginInstallModalProps {
@@ -14,13 +14,14 @@ interface PluginInstallModalProps {
const PluginInstallModal: FC<PluginInstallModalProps> = ({ artifact, version, hash, onOK, onCancel, closeModal }) => {
const [loading, setLoading] = useState<boolean>(false);
return (
- <ModalRoot
+ <ConfirmModal
bOKDisabled={loading}
closeModal={closeModal}
onOK={async () => {
setLoading(true);
await onOK();
setTimeout(() => Router.OpenQuickAccessMenu(QuickAccessTab.Decky), 250);
+ setTimeout(() => window.DeckyPluginLoader.checkPluginUpdates(), 1000);
}}
onCancel={async () => {
await onCancel();
@@ -34,7 +35,7 @@ const PluginInstallModal: FC<PluginInstallModalProps> = ({ artifact, version, ha
{!loading && '?'}
</div>
</div>
- </ModalRoot>
+ </ConfirmModal>
);
};
diff --git a/frontend/src/components/modals/filepicker/iconCustomizations.ts b/frontend/src/components/modals/filepicker/iconCustomizations.ts
new file mode 100644
index 00000000..e09c9e67
--- /dev/null
+++ b/frontend/src/components/modals/filepicker/iconCustomizations.ts
@@ -0,0 +1,170 @@
+// https://codesandbox.io/s/react-file-icon-colored-tmwut?file=/src/App.js
+import { FileIconProps } from 'react-file-icon';
+
+type T_FileExtList = string[];
+
+const styleDef: [FileIconProps, T_FileExtList][] = [];
+
+// video ////////////////////////////////////
+const videoStyle = {
+ color: '#f00f0f',
+};
+const videoExtList = [
+ 'avi',
+ '3g2',
+ '3gp',
+ 'aep',
+ 'asf',
+ 'flv',
+ 'm4v',
+ 'mkv',
+ 'mov',
+ 'mp4',
+ 'mpeg',
+ 'mpg',
+ 'ogv',
+ 'pr',
+ 'swfw',
+ 'webm',
+ 'wmv',
+ 'swf',
+ 'rm',
+];
+
+styleDef.push([videoStyle, videoExtList]);
+
+// image ////////////////////////////////////
+const imageStyle = {
+ color: '#d18f00',
+};
+
+const imageExtList = ['png', 'jpg', 'jpeg', 'gif', 'bmp', 'tif', 'tiff'];
+
+styleDef.push([imageStyle, imageExtList]);
+
+// zip ////////////////////////////////////
+const zipStyle = {
+ color: '#f7b500',
+ labelTextColor: '#000',
+ // glyphColor: "#de9400"
+};
+
+const zipExtList = ['zip', 'zipx', '7zip', 'tar', 'sitx', 'gz', 'rar'];
+
+styleDef.push([zipStyle, zipExtList]);
+
+// audio ////////////////////////////////////
+const audioStyle = {
+ color: '#f00f0f',
+};
+
+const audioExtList = ['aac', 'aif', 'aiff', 'flac', 'm4a', 'mid', 'mp3', 'ogg', 'wav'];
+
+styleDef.push([audioStyle, audioExtList]);
+
+// text ////////////////////////////////////
+const textStyle = {
+ color: '#ffffff',
+ glyphColor: '#787878',
+};
+
+const textExtList = ['cue', 'odt', 'md', 'rtf', 'txt', 'tex', 'wpd', 'wps', 'xlr', 'fodt'];
+
+styleDef.push([textStyle, textExtList]);
+
+// system ////////////////////////////////////
+const systemStyle = {
+ color: '#111',
+};
+
+const systemExtList = ['exe', 'ini', 'dll', 'plist', 'sys'];
+
+styleDef.push([systemStyle, systemExtList]);
+
+// srcCode ////////////////////////////////////
+const srcCodeStyle = {
+ glyphColor: '#787878',
+ color: '#ffffff',
+};
+
+const srcCodeExtList = [
+ 'asp',
+ 'aspx',
+ 'c',
+ 'cpp',
+ 'cs',
+ 'css',
+ 'scss',
+ 'py',
+ 'json',
+ 'htm',
+ 'html',
+ 'java',
+ 'yml',
+ 'php',
+ 'js',
+ 'ts',
+ 'rb',
+ 'jsx',
+ 'tsx',
+];
+
+styleDef.push([srcCodeStyle, srcCodeExtList]);
+
+// vector ////////////////////////////////////
+const vectorStyle = {
+ color: '#ffe600',
+};
+
+const vectorExtList = ['dwg', 'dxf', 'ps', 'svg', 'eps'];
+
+styleDef.push([vectorStyle, vectorExtList]);
+
+// font ////////////////////////////////////
+const fontStyle = {
+ color: '#555',
+};
+
+const fontExtList = ['fnt', 'ttf', 'otf', 'fon', 'eot', 'woff'];
+
+styleDef.push([fontStyle, fontExtList]);
+
+// objectModel ////////////////////////////////////
+const objectModelStyle = {
+ color: '#bf6a02',
+ glyphColor: '#bf6a02',
+};
+
+const objectModelExtList = ['3dm', '3ds', 'max', 'obj', 'pkg'];
+
+styleDef.push([objectModelStyle, objectModelExtList]);
+
+// sheet ////////////////////////////////////
+const sheetStyle = {
+ color: '#2a6e00',
+};
+
+const sheetExtList = ['csv', 'fods', 'ods', 'xlr'];
+
+styleDef.push([sheetStyle, sheetExtList]);
+
+// const defaultStyle: Record<string, FileIconProps> = {
+// pdf: {
+// glyphColor: "white",
+// color: "#D93831"
+// }
+// };
+
+//////////////////////////////////////////////////
+
+function createStyleObj(extList: T_FileExtList, styleObj: Partial<FileIconProps>) {
+ return Object.fromEntries(
+ extList.map((ext) => {
+ return [ext, { ...styleObj, glyphColor: 'white' }];
+ }),
+ );
+}
+
+export const styleDefObj = styleDef.reduce((acc, [fileStyle, fileExtList]) => {
+ return { ...acc, ...createStyleObj(fileExtList, fileStyle) };
+});
diff --git a/frontend/src/components/modals/filepicker/index.tsx b/frontend/src/components/modals/filepicker/index.tsx
new file mode 100644
index 00000000..dcf179a3
--- /dev/null
+++ b/frontend/src/components/modals/filepicker/index.tsx
@@ -0,0 +1,160 @@
+import { DialogButton, Focusable, SteamSpinner, TextField } from 'decky-frontend-lib';
+import { useEffect } from 'react';
+import { FunctionComponent, useState } from 'react';
+import { FileIcon, defaultStyles } from 'react-file-icon';
+import { FaArrowUp, FaFolder } from 'react-icons/fa';
+
+import Logger from '../../../logger';
+import { styleDefObj } from './iconCustomizations';
+
+const logger = new Logger('FilePicker');
+
+export interface FilePickerProps {
+ startPath: string;
+ includeFiles?: boolean;
+ regex?: RegExp;
+ onSubmit: (val: { path: string; realpath: string }) => void;
+ closeModal?: () => void;
+}
+
+interface File {
+ isdir: boolean;
+ name: string;
+ realpath: string;
+}
+
+interface FileListing {
+ realpath: string;
+ files: File[];
+}
+
+function getList(
+ path: string,
+ includeFiles: boolean = true,
+): Promise<{ result: FileListing | string; success: boolean }> {
+ return window.DeckyPluginLoader.callServerMethod('filepicker_ls', { path, include_files: includeFiles });
+}
+
+const iconStyles = {
+ paddingRight: '10px',
+ width: '1em',
+};
+
+const FilePicker: FunctionComponent<FilePickerProps> = ({
+ startPath,
+ includeFiles = true,
+ regex,
+ onSubmit,
+ closeModal,
+}) => {
+ if (startPath.endsWith('/')) startPath = startPath.substring(0, startPath.length - 1); // remove trailing path
+ const [path, setPath] = useState<string>(startPath);
+ const [listing, setListing] = useState<FileListing>({ files: [], realpath: path });
+ const [error, setError] = useState<string | null>(null);
+ const [loading, setLoading] = useState<boolean>(true);
+
+ useEffect(() => {
+ (async () => {
+ if (error) setError(null);
+ setLoading(true);
+ const listing = await getList(path, includeFiles);
+ if (!listing.success) {
+ setListing({ files: [], realpath: path });
+ setLoading(false);
+ setError(listing.result as string);
+ logger.error(listing.result);
+ return;
+ }
+ setLoading(false);
+ setListing(listing.result as FileListing);
+ logger.log('reloaded', path, listing);
+ })();
+ }, [path]);
+
+ return (
+ <div className="deckyFilePicker">
+ <Focusable style={{ display: 'flex', flexDirection: 'row', paddingBottom: '10px' }}>
+ <DialogButton
+ style={{
+ minWidth: 'unset',
+ width: '40px',
+ flexGrow: '0',
+ borderRadius: 'unset',
+ margin: '0',
+ padding: '10px',
+ }}
+ onClick={() => {
+ const newPathArr = path.split('/');
+ newPathArr.pop();
+ let newPath = newPathArr.join('/');
+ if (newPath == '') newPath = '/';
+ setPath(newPath);
+ }}
+ >
+ <FaArrowUp />
+ </DialogButton>
+ <div style={{ flexGrow: '1', width: '100%' }}>
+ <TextField
+ value={path}
+ onChange={(e) => {
+ e.target.value && setPath(e.target.value);
+ }}
+ style={{ height: '100%' }}
+ />
+ </div>
+ </Focusable>
+ <Focusable style={{ display: 'flex', flexDirection: 'column', height: '60vh', overflow: 'scroll' }}>
+ {loading && <SteamSpinner style={{ height: '100%' }} />}
+ {!loading &&
+ listing.files
+ .filter((file) => (includeFiles || file.isdir) && (!regex || regex.test(file.name)))
+ .map((file) => {
+ let extension = file.realpath.split('.').pop() as string;
+ return (
+ <DialogButton
+ style={{ borderRadius: 'unset', margin: '0', padding: '10px' }}
+ onClick={() => {
+ const fullPath = `${path}${path.endsWith('/') ? '' : '/'}${file.name}`;
+ if (file.isdir) setPath(fullPath);
+ else {
+ onSubmit({ path: fullPath, realpath: file.realpath });
+ closeModal?.();
+ }
+ }}
+ >
+ <div style={{ display: 'flex', flexDirection: 'row', alignItems: 'flex-start' }}>
+ {file.isdir ? (
+ <FaFolder style={iconStyles} />
+ ) : (
+ <div style={iconStyles}>
+ {file.realpath.includes('.') ? (
+ <FileIcon {...defaultStyles[extension]} {...styleDefObj[extension]} extension={''} />
+ ) : (
+ <FileIcon />
+ )}
+ </div>
+ )}
+ {file.name}
+ </div>
+ </DialogButton>
+ );
+ })}
+ {error}
+ </Focusable>
+ {!loading && !error && !includeFiles && (
+ <DialogButton
+ className="Primary"
+ style={{ marginTop: '10px', alignSelf: 'flex-end' }}
+ onClick={() => {
+ onSubmit({ path, realpath: listing.realpath });
+ closeModal?.();
+ }}
+ >
+ Use this folder
+ </DialogButton>
+ )}
+ </div>
+ );
+};
+
+export default FilePicker;
diff --git a/frontend/src/components/modals/filepicker/patches/README.md b/frontend/src/components/modals/filepicker/patches/README.md
new file mode 100644
index 00000000..154914c5
--- /dev/null
+++ b/frontend/src/components/modals/filepicker/patches/README.md
@@ -0,0 +1 @@
+This directory contains patches that replace Valve's broken file picker with ours.
diff --git a/frontend/src/components/modals/filepicker/patches/index.ts b/frontend/src/components/modals/filepicker/patches/index.ts
new file mode 100644
index 00000000..310bfbf8
--- /dev/null
+++ b/frontend/src/components/modals/filepicker/patches/index.ts
@@ -0,0 +1,10 @@
+import library from './library';
+let patches: Function[] = [];
+
+export function deinitFilepickerPatches() {
+ patches.forEach((unpatch) => unpatch());
+}
+
+export async function initFilepickerPatches() {
+ patches.push(await library());
+}
diff --git a/frontend/src/components/modals/filepicker/patches/library.ts b/frontend/src/components/modals/filepicker/patches/library.ts
new file mode 100644
index 00000000..c9c7d53c
--- /dev/null
+++ b/frontend/src/components/modals/filepicker/patches/library.ts
@@ -0,0 +1,57 @@
+import { Patch, findModuleChild, replacePatch } from 'decky-frontend-lib';
+
+declare global {
+ interface Window {
+ SteamClient: any;
+ appDetailsStore: any;
+ }
+}
+
+let patch: Patch;
+
+function rePatch() {
+ // If you patch anything on SteamClient within the first few seconds of the client having loaded it will get redefined for some reason, so repatch any of these changes that occur within the first 20s of the last patch
+ patch = replacePatch(window.SteamClient.Apps, 'PromptToChangeShortcut', async ([appid]: number[]) => {
+ try {
+ const details = window.appDetailsStore.GetAppDetails(appid);
+ console.log(details);
+ // strShortcutStartDir
+ const file = await window.DeckyPluginLoader.openFilePicker(details.strShortcutStartDir.replaceAll('"', ''));
+ console.log('user selected', file);
+ window.SteamClient.Apps.SetShortcutExe(appid, JSON.stringify(file.path));
+ const pathArr = file.path.split('/');
+ pathArr.pop();
+ const folder = pathArr.join('/');
+ window.SteamClient.Apps.SetShortcutStartDir(appid, JSON.stringify(folder));
+ } catch (e) {
+ console.error(e);
+ }
+ });
+}
+
+// TODO type and add to frontend-lib
+const History = findModuleChild((m) => {
+ if (typeof m !== 'object') return undefined;
+ for (let prop in m) {
+ if (m[prop]?.m_history) return m[prop].m_history;
+ }
+});
+
+export default async function libraryPatch() {
+ try {
+ rePatch();
+ const unlisten = History.listen(() => {
+ if (window.SteamClient.Apps.PromptToChangeShortcut !== patch.patchedFunction) {
+ rePatch();
+ }
+ });
+
+ return () => {
+ patch.unpatch();
+ unlisten();
+ };
+ } catch (e) {
+ console.error('Error patching library file picker', e);
+ }
+ return () => {};
+}
diff --git a/frontend/src/components/settings/pages/general/BranchSelect.tsx b/frontend/src/components/settings/pages/general/BranchSelect.tsx
index d803f604..154bff9c 100644
--- a/frontend/src/components/settings/pages/general/BranchSelect.tsx
+++ b/frontend/src/components/settings/pages/general/BranchSelect.tsx
@@ -1,9 +1,12 @@
import { Dropdown, Field } from 'decky-frontend-lib';
import { FunctionComponent } from 'react';
+import Logger from '../../../../logger';
import { callUpdaterMethod } from '../../../../updater';
import { useSetting } from '../../../../utils/hooks/useSetting';
+const logger = new Logger('BranchSelect');
+
enum UpdateBranch {
Stable,
Prerelease,
@@ -28,7 +31,7 @@ const BranchSelect: FunctionComponent<{}> = () => {
onChange={async (newVal) => {
await setSelectedBranch(newVal.data);
callUpdaterMethod('check_for_updates');
- console.log('switching branches!');
+ logger.log('switching branches!');
}}
/>
</Field>
diff --git a/frontend/src/components/settings/pages/general/Updater.tsx b/frontend/src/components/settings/pages/general/Updater.tsx
index 7056ed13..b4ea8536 100644
--- a/frontend/src/components/settings/pages/general/Updater.tsx
+++ b/frontend/src/components/settings/pages/general/Updater.tsx
@@ -1,4 +1,13 @@
-import { Carousel, DialogButton, Field, Focusable, ProgressBarWithInfo, Spinner, showModal } from 'decky-frontend-lib';
+import {
+ Carousel,
+ DialogButton,
+ Field,
+ FocusRing,
+ Focusable,
+ ProgressBarWithInfo,
+ Spinner,
+ showModal,
+} from 'decky-frontend-lib';
import { useCallback } from 'react';
import { Suspense, lazy } from 'react';
import { useEffect, useState } from 'react';
@@ -7,49 +16,48 @@ import { FaArrowDown } from 'react-icons/fa';
import { VerInfo, callUpdaterMethod, finishUpdate } from '../../../../updater';
import { useDeckyState } from '../../../DeckyState';
import InlinePatchNotes from '../../../patchnotes/InlinePatchNotes';
+import WithSuspense from '../../../WithSuspense';
const MarkdownRenderer = lazy(() => import('../../../Markdown'));
-// import ReactMarkdown from 'react-markdown'
-// import remarkGfm from 'remark-gfm'
-
function PatchNotesModal({ versionInfo, closeModal }: { versionInfo: VerInfo | null; closeModal?: () => {} }) {
return (
<Focusable onCancelButton={closeModal}>
- <Carousel
- fnItemRenderer={(id: number) => (
- <Focusable
- onActivate={() => {}}
- style={{
- marginTop: '40px',
- height: 'calc( 100% - 40px )',
- overflowY: 'scroll',
- display: 'flex',
- justifyContent: 'center',
- margin: '40px',
- }}
- >
- <div>
- <h1>{versionInfo?.all?.[id]?.name}</h1>
- {versionInfo?.all?.[id]?.body ? (
- <Suspense fallback={<Spinner style={{ width: '24', height: '24' }} />}>
- <MarkdownRenderer>{versionInfo.all[id].body}</MarkdownRenderer>
- </Suspense>
- ) : (
- 'no patch notes for this version'
- )}
- </div>
- </Focusable>
- )}
- fnGetId={(id) => id}
- nNumItems={versionInfo?.all?.length}
- nHeight={window.innerHeight - 150}
- nItemHeight={window.innerHeight - 200}
- nItemMarginX={0}
- initialColumn={0}
- autoFocus={true}
- fnGetColumnWidth={() => window.innerWidth}
- />
+ <FocusRing>
+ <Carousel
+ fnItemRenderer={(id: number) => (
+ <Focusable
+ style={{
+ marginTop: '40px',
+ height: 'calc( 100% - 40px )',
+ overflowY: 'scroll',
+ display: 'flex',
+ justifyContent: 'center',
+ margin: '40px',
+ }}
+ >
+ <div>
+ <h1>{versionInfo?.all?.[id]?.name}</h1>
+ {versionInfo?.all?.[id]?.body ? (
+ <WithSuspense>
+ <MarkdownRenderer onDismiss={closeModal}>{versionInfo.all[id].body}</MarkdownRenderer>
+ </WithSuspense>
+ ) : (
+ 'no patch notes for this version'
+ )}
+ </div>
+ </Focusable>
+ )}
+ fnGetId={(id) => id}
+ nNumItems={versionInfo?.all?.length}
+ nHeight={window.innerHeight - 40}
+ nItemHeight={window.innerHeight - 40}
+ nItemMarginX={0}
+ initialColumn={0}
+ autoFocus={true}
+ fnGetColumnWidth={() => window.innerWidth}
+ />
+ </FocusRing>
</Focusable>
);
}
@@ -126,7 +134,7 @@ export default function UpdaterSettings() {
) : (
<ProgressBarWithInfo
layout="inline"
- bottomSeparator={false}
+ bottomSeparator="none"
nProgress={updateProgress}
indeterminate={reloading}
sOperationText={reloading ? 'Reloading' : 'Updating'}
diff --git a/frontend/src/components/store/PluginCard.tsx b/frontend/src/components/store/PluginCard.tsx
index a6e9458a..0155ff99 100644
--- a/frontend/src/components/store/PluginCard.tsx
+++ b/frontend/src/components/store/PluginCard.tsx
@@ -113,8 +113,22 @@ const PluginCard: FC<PluginCardProps> = ({ plugin }) => {
}}
className="deckyStoreCardInfo"
>
- <p className={joinClassNames(staticClasses.PanelSectionRow)}>
- <span>Author: {plugin.author}</span>
+ <p
+ className={joinClassNames(staticClasses.PanelSectionRow)}
+ style={{ marginTop: '0px', marginLeft: '16px' }}
+ >
+ <span style={{ paddingLeft: '0px' }}>Author: {plugin.author}</span>
+ </p>
+ <p
+ className={joinClassNames(staticClasses.PanelSectionRow)}
+ style={{
+ marginLeft: '16px',
+ marginTop: '0px',
+ marginBottom: '0px',
+ marginRight: '16px',
+ }}
+ >
+ <span style={{ paddingLeft: '0px' }}>{plugin.description}</span>
</p>
<p
className={joinClassNames('deckyStoreCardTagsContainer', staticClasses.PanelSectionRow)}
diff --git a/frontend/src/components/store/Store.tsx b/frontend/src/components/store/Store.tsx
index fd582edd..cb6f34ad 100644
--- a/frontend/src/components/store/Store.tsx
+++ b/frontend/src/components/store/Store.tsx
@@ -1,9 +1,12 @@
import { SteamSpinner } from 'decky-frontend-lib';
import { FC, useEffect, useState } from 'react';
+import Logger from '../../logger';
import { LegacyStorePlugin, StorePlugin, getLegacyPluginList, getPluginList } from '../../store';
import PluginCard from './PluginCard';
+const logger = new Logger('FilePicker');
+
const StorePage: FC<{}> = () => {
const [data, setData] = useState<StorePlugin[] | null>(null);
const [legacyData, setLegacyData] = useState<LegacyStorePlugin[] | null>(null);
@@ -11,12 +14,12 @@ const StorePage: FC<{}> = () => {
useEffect(() => {
(async () => {
const res = await getPluginList();
- console.log(res);
+ logger.log('got data!', res);
setData(res);
})();
(async () => {
const res = await getLegacyPluginList();
- console.log(res);
+ logger.log('got legacy data!', res);
setLegacyData(res);
})();
}, []);