summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--frontend/assets/plugin_store.pngbin0 -> 56506 bytes
-rw-r--r--frontend/index.d.ts2
-rw-r--r--frontend/package.json3
-rw-r--r--frontend/pnpm-lock.yaml44
-rw-r--r--frontend/rollup.config.js2
-rw-r--r--frontend/src/components/modals/PluginInstallModal.tsx17
-rw-r--r--frontend/src/components/store/PluginCard.tsx277
-rw-r--r--frontend/src/components/store/Store.tsx224
-rw-r--r--frontend/src/plugin-loader.tsx17
-rw-r--r--frontend/tsconfig.json2
10 files changed, 397 insertions, 191 deletions
diff --git a/frontend/assets/plugin_store.png b/frontend/assets/plugin_store.png
new file mode 100644
index 00000000..17832cab
--- /dev/null
+++ b/frontend/assets/plugin_store.png
Binary files differ
diff --git a/frontend/index.d.ts b/frontend/index.d.ts
new file mode 100644
index 00000000..3e1bef28
--- /dev/null
+++ b/frontend/index.d.ts
@@ -0,0 +1,2 @@
+declare module '*.png';
+declare module '*.jpg';
diff --git a/frontend/package.json b/frontend/package.json
index 9001a472..1501f31d 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -12,6 +12,7 @@
},
"devDependencies": {
"@rollup/plugin-commonjs": "^21.1.0",
+ "@rollup/plugin-image": "^3.0.1",
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "^13.3.0",
"@rollup/plugin-replace": "^4.0.0",
@@ -41,7 +42,7 @@
}
},
"dependencies": {
- "decky-frontend-lib": "^3.18.9",
+ "decky-frontend-lib": "^3.18.10",
"react-file-icon": "^1.2.0",
"react-icons": "^4.4.0",
"react-markdown": "^8.0.3",
diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml
index 4ba64804..6e6dc3c0 100644
--- a/frontend/pnpm-lock.yaml
+++ b/frontend/pnpm-lock.yaml
@@ -2,6 +2,7 @@ lockfileVersion: 5.4
specifiers:
'@rollup/plugin-commonjs': ^21.1.0
+ '@rollup/plugin-image': ^3.0.1
'@rollup/plugin-json': ^4.1.0
'@rollup/plugin-node-resolve': ^13.3.0
'@rollup/plugin-replace': ^4.0.0
@@ -10,7 +11,7 @@ specifiers:
'@types/react-file-icon': ^1.0.1
'@types/react-router': 5.1.18
'@types/webpack': ^5.28.0
- decky-frontend-lib: ^3.18.9
+ decky-frontend-lib: ^3.18.10
husky: ^8.0.1
import-sort-style-module: ^6.0.0
inquirer: ^8.2.4
@@ -30,7 +31,7 @@ specifiers:
typescript: ^4.7.4
dependencies:
- decky-frontend-lib: 3.18.9
+ decky-frontend-lib: 3.18.10
react-file-icon: 1.2.0_wcqkhtmu7mswc6yz4uyexck3ty
react-icons: 4.4.0_react@16.14.0
react-markdown: 8.0.3_vshvapmxg47tngu7tvrsqpq55u
@@ -38,6 +39,7 @@ dependencies:
devDependencies:
'@rollup/plugin-commonjs': 21.1.0_rollup@2.76.0
+ '@rollup/plugin-image': 3.0.1_rollup@2.76.0
'@rollup/plugin-json': 4.1.0_rollup@2.76.0
'@rollup/plugin-node-resolve': 13.3.0_rollup@2.76.0
'@rollup/plugin-replace': 4.0.0_rollup@2.76.0
@@ -339,6 +341,20 @@ packages:
rollup: 2.76.0
dev: true
+ /@rollup/plugin-image/3.0.1_rollup@2.76.0:
+ resolution: {integrity: sha512-F50Sko4Xcc576x7HG9f3MvJKKnBfSmqfVFWJkJgyIEkI8YxZxux28lDbuy0+GsAK6BFl9Gn+TRXOUgHHJbFh3w==}
+ engines: {node: '>=14.0.0'}
+ peerDependencies:
+ rollup: ^1.20.0||^2.0.0||^3.0.0
+ peerDependenciesMeta:
+ rollup:
+ optional: true
+ dependencies:
+ '@rollup/pluginutils': 5.0.2_rollup@2.76.0
+ mini-svg-data-uri: 1.4.4
+ rollup: 2.76.0
+ dev: true
+
/@rollup/plugin-inject/4.0.4_rollup@2.76.0:
resolution: {integrity: sha512-4pbcU4J/nS+zuHk+c+OL3WtmEQhqxlZ9uqfjQMQDOHOPld7PsCd8k5LWs8h5wjwJN7MgnAn768F2sDxEP4eNFQ==}
peerDependencies:
@@ -422,6 +438,21 @@ packages:
picomatch: 2.3.1
dev: true
+ /@rollup/pluginutils/5.0.2_rollup@2.76.0:
+ resolution: {integrity: sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==}
+ engines: {node: '>=14.0.0'}
+ peerDependencies:
+ rollup: ^1.20.0||^2.0.0||^3.0.0
+ peerDependenciesMeta:
+ rollup:
+ optional: true
+ dependencies:
+ '@types/estree': 1.0.0
+ estree-walker: 2.0.2
+ picomatch: 2.3.1
+ rollup: 2.76.0
+ dev: true
+
/@types/debug/4.1.7:
resolution: {integrity: sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==}
dependencies:
@@ -944,8 +975,8 @@ packages:
dependencies:
ms: 2.1.2
- /decky-frontend-lib/3.18.9:
- resolution: {integrity: sha512-QNMHDDAHfL+JpvVVte4Vj8iyOqvz/2iyFEknbJ1/Kz7aPTygFUsJp5mq1FDVvVNjfCYfF3fYAaZVqZu3d7pCEA==}
+ /decky-frontend-lib/3.18.10:
+ resolution: {integrity: sha512-2mgbA3sSkuwQR/FnmhXVrcW6LyTS95IuL6muJAmQCruhBvXapDtjk1TcgxqMZxFZwGD1IPnemPYxHZll6IgnZw==}
dev: false
/decode-named-character-reference/1.0.2:
@@ -1936,6 +1967,11 @@ packages:
engines: {node: '>=6'}
dev: true
+ /mini-svg-data-uri/1.4.4:
+ resolution: {integrity: sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==}
+ hasBin: true
+ dev: true
+
/minimatch/3.1.2:
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
dependencies:
diff --git a/frontend/rollup.config.js b/frontend/rollup.config.js
index fc924c36..46479295 100644
--- a/frontend/rollup.config.js
+++ b/frontend/rollup.config.js
@@ -1,4 +1,5 @@
import commonjs from '@rollup/plugin-commonjs';
+import image from '@rollup/plugin-image';
import json from '@rollup/plugin-json';
import { nodeResolve } from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
@@ -29,6 +30,7 @@ export default defineConfig({
preventAssignment: false,
'process.env.NODE_ENV': JSON.stringify('production'),
}),
+ image(),
],
preserveEntrySignatures: false,
output: {
diff --git a/frontend/src/components/modals/PluginInstallModal.tsx b/frontend/src/components/modals/PluginInstallModal.tsx
index dfddc199..f2f13bbf 100644
--- a/frontend/src/components/modals/PluginInstallModal.tsx
+++ b/frontend/src/components/modals/PluginInstallModal.tsx
@@ -1,4 +1,4 @@
-import { ConfirmModal, Navigation, QuickAccessTab, Spinner, staticClasses } from 'decky-frontend-lib';
+import { ConfirmModal, Navigation, QuickAccessTab } from 'decky-frontend-lib';
import { FC, useState } from 'react';
interface PluginInstallModalProps {
@@ -26,15 +26,14 @@ const PluginInstallModal: FC<PluginInstallModalProps> = ({ artifact, version, ha
onCancel={async () => {
await onCancel();
}}
+ strTitle={`Install ${artifact}`}
+ strOKButtonText={loading ? 'Installing' : 'Install'}
>
- <div className={staticClasses.Title} style={{ flexDirection: 'column' }}>
- {hash == 'False' ? <h3 style={{ color: 'red' }}>!!!!NO HASH PROVIDED!!!!</h3> : null}
- <div style={{ flexDirection: 'row' }}>
- {loading && <Spinner style={{ width: '20px' }} />} {loading ? 'Installing' : 'Install'} {artifact}
- {version ? ' version ' + version : null}
- {!loading && '?'}
- </div>
- </div>
+ {hash == 'False' ? (
+ <h3 style={{ color: 'red' }}>!!!!NO HASH PROVIDED!!!!</h3>
+ ) : (
+ `Are you sure you want to install ${artifact} ${version}?`
+ )}
</ConfirmModal>
);
};
diff --git a/frontend/src/components/store/PluginCard.tsx b/frontend/src/components/store/PluginCard.tsx
index aa5fd1d6..828d3ae9 100644
--- a/frontend/src/components/store/PluginCard.tsx
+++ b/frontend/src/components/store/PluginCard.tsx
@@ -1,15 +1,12 @@
import {
- DialogButton,
+ ButtonItem,
Dropdown,
Focusable,
- Navigation,
- QuickAccessTab,
+ PanelSectionRow,
SingleDropdownOption,
SuspensefulImage,
- joinClassNames,
- staticClasses,
} from 'decky-frontend-lib';
-import { FC, useRef, useState } from 'react';
+import { FC, useState } from 'react';
import { StorePlugin, StorePluginVersion, requestPluginInstall } from '../../store';
@@ -19,172 +16,162 @@ interface PluginCardProps {
const PluginCard: FC<PluginCardProps> = ({ plugin }) => {
const [selectedOption, setSelectedOption] = useState<number>(0);
- const buttonRef = useRef<HTMLDivElement>(null);
- const containerRef = useRef<HTMLDivElement>(null);
+ const root: boolean = plugin.tags.some((tag) => tag === 'root');
+
return (
<div
+ className="deckyStoreCard"
style={{
- padding: '30px',
- paddingTop: '10px',
- paddingBottom: '10px',
+ marginLeft: '20px',
+ marginRight: '20px',
+ marginBottom: '20px',
+ display: 'flex',
+ alignItems: 'center',
}}
>
- {/* TODO: abstract this messy focus hackiness into a custom component in lib */}
- <Focusable
- className="deckyStoreCard"
- ref={containerRef}
- onActivate={(_: CustomEvent) => {
- buttonRef.current!.focus();
- }}
- onCancel={(_: CustomEvent) => {
- if (containerRef.current!.querySelectorAll('* :focus').length === 0) {
- Navigation.NavigateBack();
- setTimeout(() => Navigation.OpenQuickAccessMenu(QuickAccessTab.Decky), 1000);
- } else {
- containerRef.current!.focus();
- }
+ <div
+ className="deckyStoreCardImageContainer"
+ style={{
+ width: '320px',
+ height: '200px',
+ position: 'relative',
}}
+ >
+ <SuspensefulImage
+ className="deckyStoreCardImage"
+ suspenseHeight="200px"
+ suspenseWidth="320px"
+ style={{
+ width: '320px',
+ height: '200px',
+ objectFit: 'cover',
+ }}
+ src={plugin.image_url}
+ />
+ </div>
+ <div
+ className="deckyStoreCardInfo"
style={{
+ width: 'calc(100% - 320px)', // The calc is here so that the info section doesn't expand into the image
display: 'flex',
flexDirection: 'column',
- background: '#ACB2C924',
- height: 'unset',
- marginBottom: 'unset',
- // boxShadow: var(--gpShadow-Medium);
- scrollSnapAlign: 'start',
- boxSizing: 'border-box',
+ height: '100%',
+ marginLeft: '1em',
+ justifyContent: 'center',
}}
>
- <div className="deckyStoreCardHeader" style={{ display: 'flex', alignItems: 'center' }}>
- <div
- style={{ fontSize: '18pt', padding: '10px' }}
- className={joinClassNames(staticClasses.Text)}
- // onClick={() => Router.NavigateToExternalWeb('https://github.com/' + plugin.artifact)}
- >
- {plugin.name}
- </div>
- </div>
- <div
+ <span
+ className="deckyStoreCardTitle"
style={{
- display: 'flex',
- flexDirection: 'row',
- margin: '0 0 0 10px',
+ fontSize: '1.25em',
+ fontWeight: 'bold',
+ whiteSpace: 'nowrap',
+ overflow: 'hidden',
+ textOverflow: 'ellipsis',
+ width: '90%',
}}
- className="deckyStoreCardBody"
>
- <SuspensefulImage
- className="deckyStoreCardImage"
- suspenseWidth="256px"
- style={{
- width: 'auto',
- height: '160px',
- borderRadius: '5px',
- }}
- src={plugin.image_url}
- />
- <div
+ {plugin.name}
+ </span>
+ <span
+ className="deckyStoreCardAuthor"
+ style={{
+ marginRight: 'auto',
+ fontSize: '1em',
+ }}
+ >
+ {plugin.author}
+ </span>
+ <span
+ className="deckyStoreCardDescription"
+ style={{
+ fontSize: '13px',
+ color: '#969696',
+ WebkitLineClamp: root ? '2' : '3',
+ WebkitBoxOrient: 'vertical',
+ overflow: 'hidden',
+ display: '-webkit-box',
+ }}
+ >
+ {plugin.description ? (
+ plugin.description
+ ) : (
+ <span>
+ <i style={{ color: '#666' }}>No description provided.</i>
+ </span>
+ )}
+ </span>
+ {root && (
+ <span
+ className="deckyStoreCardDescription deckyStoreCardDescriptionRoot"
style={{
- display: 'flex',
- flexDirection: 'column',
+ fontSize: '13px',
+ color: '#fee75c',
}}
- className="deckyStoreCardInfo"
>
- <p
- className={joinClassNames(staticClasses.PanelSectionRow)}
- style={{ marginTop: '0px', marginLeft: '16px' }}
- >
- <span style={{ paddingLeft: '0px' }}>Author: {plugin.author}</span>
- </p>
- <p
- className={joinClassNames(staticClasses.PanelSectionRow)}
+ <i>This plugin has full access to your Steam Deck.</i>{' '}
+ <a
+ className="deckyStoreCardDescriptionRootLink"
+ href="https://deckbrew.xyz/root"
+ target="_blank"
style={{
- marginLeft: '16px',
- marginTop: '0px',
- marginBottom: '0px',
- marginRight: '16px',
+ color: '#fee75c',
+ textDecoration: 'none',
}}
>
- <span style={{ paddingLeft: '0px' }}>{plugin.description}</span>
- </p>
- <p
- className={joinClassNames('deckyStoreCardTagsContainer', staticClasses.PanelSectionRow)}
- style={{
- padding: '0 16px',
- display: 'flex',
- flexWrap: 'wrap',
- gap: '5px 10px',
- }}
- >
- <span style={{ padding: '5px 0' }}>Tags:</span>
- {plugin.tags.map((tag: string) => (
- <span
- className="deckyStoreCardTag"
- style={{
- padding: '5px',
- borderRadius: '5px',
- background: tag == 'root' ? '#842029' : '#ACB2C947',
- }}
- >
- {tag == 'root' ? 'Requires root' : tag}
- </span>
- ))}
- </p>
- </div>
- </div>
+ deckbrew.xyz/root
+ </a>
+ </span>
+ )}
<div
- className="deckyStoreCardActionsContainer"
+ className="deckyStoreCardButtonRow"
style={{
+ marginTop: '1em',
width: '100%',
- alignSelf: 'flex-end',
- display: 'flex',
- flexDirection: 'row',
+ overflow: 'hidden',
}}
>
- <Focusable
- className="deckyStoreCardActions"
- style={{
- display: 'flex',
- flexDirection: 'row',
- width: '100%',
- margin: '10px',
- }}
- >
- <div
- className="deckyStoreCardInstallButtonContainer"
- style={{
- flex: '1',
- margin: '0 10px 0 0',
- }}
- >
- <DialogButton
- className="deckyStoreCardInstallButton"
- ref={buttonRef}
- onClick={() => requestPluginInstall(plugin.name, plugin.versions[selectedOption])}
+ <PanelSectionRow>
+ <Focusable style={{ display: 'flex', maxWidth: '100%' }}>
+ <div
+ className="deckyStoreCardInstallContainer"
+ style={{
+ paddingTop: '0px',
+ paddingBottom: '0px',
+ width: '40%',
+ }}
>
- Install
- </DialogButton>
- </div>
- <div
- className="deckyStoreCardVersionDropdownContainer"
- style={{
- flex: '0.2',
- }}
- >
- <Dropdown
- rgOptions={
- plugin.versions.map((version: StorePluginVersion, index) => ({
- data: index,
- label: version.name,
- })) as SingleDropdownOption[]
- }
- strDefaultLabel={'Select a version'}
- selectedOption={selectedOption}
- onChange={({ data }) => setSelectedOption(data)}
- />
- </div>
- </Focusable>
+ <ButtonItem
+ bottomSeparator="none"
+ layout="below"
+ onClick={() => requestPluginInstall(plugin.name, plugin.versions[selectedOption])}
+ >
+ <span className="deckyStoreCardInstallText">Install</span>
+ </ButtonItem>
+ </div>
+ <div
+ className="deckyStoreCardVersionContainer"
+ style={{
+ marginLeft: '5%',
+ width: '30%',
+ }}
+ >
+ <Dropdown
+ rgOptions={
+ plugin.versions.map((version: StorePluginVersion, index) => ({
+ data: index,
+ label: version.name,
+ })) as SingleDropdownOption[]
+ }
+ menuLabel="Plugin Version"
+ selectedOption={selectedOption}
+ onChange={({ data }) => setSelectedOption(data)}
+ />
+ </div>
+ </Focusable>
+ </PanelSectionRow>
</div>
- </Focusable>
+ </div>
</div>
);
};
diff --git a/frontend/src/components/store/Store.tsx b/frontend/src/components/store/Store.tsx
index 2ffd8efb..7a9c0e33 100644
--- a/frontend/src/components/store/Store.tsx
+++ b/frontend/src/components/store/Store.tsx
@@ -1,6 +1,16 @@
-import { SteamSpinner } from 'decky-frontend-lib';
-import { FC, useEffect, useState } from 'react';
+import {
+ Dropdown,
+ DropdownOption,
+ Focusable,
+ PanelSectionRow,
+ SteamSpinner,
+ Tabs,
+ TextField,
+ findModule,
+} from 'decky-frontend-lib';
+import { FC, useEffect, useMemo, useState } from 'react';
+import logo from '../../../assets/plugin_store.png';
import Logger from '../../logger';
import { StorePlugin, getPluginList } from '../../store';
import PluginCard from './PluginCard';
@@ -8,7 +18,12 @@ import PluginCard from './PluginCard';
const logger = new Logger('FilePicker');
const StorePage: FC<{}> = () => {
+ const [currentTabRoute, setCurrentTabRoute] = useState<string>('browse');
const [data, setData] = useState<StorePlugin[] | null>(null);
+ const { TabCount } = findModule((m) => {
+ if (m?.TabCount && m?.TabTitle) return true;
+ return false;
+ });
useEffect(() => {
(async () => {
@@ -19,19 +34,12 @@ const StorePage: FC<{}> = () => {
}, []);
return (
- <div
- style={{
- marginTop: '40px',
- height: 'calc( 100% - 40px )',
- overflowY: 'scroll',
- }}
- >
+ <>
<div
style={{
- display: 'flex',
- flexWrap: 'nowrap',
- flexDirection: 'column',
- height: '100%',
+ marginTop: '40px',
+ height: 'calc( 100% - 40px )',
+ background: '#0005',
}}
>
{!data ? (
@@ -39,13 +47,193 @@ const StorePage: FC<{}> = () => {
<SteamSpinner />
</div>
) : (
- <div>
- {data.map((plugin: StorePlugin) => (
- <PluginCard plugin={plugin} />
- ))}
- </div>
+ <Tabs
+ activeTab={currentTabRoute}
+ onShowTab={(tabId: string) => {
+ setCurrentTabRoute(tabId);
+ }}
+ tabs={[
+ {
+ title: 'Browse',
+ content: <BrowseTab children={{ data: data }} />,
+ id: 'browse',
+ renderTabAddon: () => <span className={TabCount}>{data.length}</span>,
+ },
+ {
+ title: 'About',
+ content: <AboutTab />,
+ id: 'about',
+ },
+ ]}
+ />
)}
</div>
+ </>
+ );
+};
+
+const BrowseTab: FC<{ children: { data: StorePlugin[] } }> = (data) => {
+ const sortOptions = useMemo(
+ (): DropdownOption[] => [
+ { data: 1, label: 'Alphabetical (A to Z)' },
+ { data: 2, label: 'Alphabetical (Z to A)' },
+ ],
+ [],
+ );
+
+ // const filterOptions = useMemo((): DropdownOption[] => [{ data: 1, label: 'All' }], []);
+
+ const [selectedSort, setSort] = useState<number>(sortOptions[0].data);
+ // const [selectedFilter, setFilter] = useState<number>(filterOptions[0].data);
+ const [searchFieldValue, setSearchValue] = useState<string>('');
+
+ return (
+ <>
+ <style>{`
+ .deckyStoreCardInstallContainer > .Panel {
+ padding: 0;
+ }
+ `}</style>
+ {/* This should be used once filtering is added
+
+ <PanelSectionRow>
+ <Focusable style={{ display: 'flex', maxWidth: '100%' }}>
+ <div
+ style={{
+ display: 'flex',
+ flexDirection: 'column',
+ width: '47.5%',
+ }}
+ >
+ <span className="DialogLabel">Sort</span>
+ <Dropdown
+ menuLabel="Sort"
+ rgOptions={sortOptions}
+ strDefaultLabel="Last Updated (Newest)"
+ selectedOption={selectedSort}
+ onChange={(e) => setSort(e.data)}
+ />
+ </div>
+ <div
+ style={{
+ display: 'flex',
+ flexDirection: 'column',
+ width: '47.5%',
+ marginLeft: 'auto',
+ }}
+ >
+ <span className="DialogLabel">Filter</span>
+ <Dropdown
+ menuLabel="Filter"
+ rgOptions={filterOptions}
+ strDefaultLabel="All"
+ selectedOption={selectedFilter}
+ onChange={(e) => setFilter(e.data)}
+ />
+ </div>
+ </Focusable>
+ </PanelSectionRow>
+ <div style={{ justifyContent: 'center', display: 'flex' }}>
+ <Focusable style={{ display: 'flex', alignItems: 'center', width: '96%' }}>
+ <div style={{ width: '100%' }}>
+ <TextField label="Search" value={searchFieldValue} onChange={(e) => setSearchValue(e.target.value)} />
+ </div>
+ </Focusable>
+ </div>
+ */}
+ <PanelSectionRow>
+ <Focusable style={{ display: 'flex', maxWidth: '100%' }}>
+ <div
+ style={{
+ display: 'flex',
+ flexDirection: 'column',
+ minWidth: '100%',
+ maxWidth: '100%',
+ }}
+ >
+ <span className="DialogLabel">Sort</span>
+ <Dropdown
+ menuLabel="Sort"
+ rgOptions={sortOptions}
+ strDefaultLabel="Last Updated (Newest)"
+ selectedOption={selectedSort}
+ onChange={(e) => setSort(e.data)}
+ />
+ </div>
+ </Focusable>
+ </PanelSectionRow>
+ <div style={{ justifyContent: 'center', display: 'flex' }}>
+ <Focusable style={{ display: 'flex', alignItems: 'center', width: '96%' }}>
+ <div style={{ width: '100%' }}>
+ <TextField label="Search" value={searchFieldValue} onChange={(e) => setSearchValue(e.target.value)} />
+ </div>
+ </Focusable>
+ </div>
+ <div>
+ {data.children.data
+ .filter((plugin: StorePlugin) => {
+ return (
+ plugin.name.toLowerCase().includes(searchFieldValue.toLowerCase()) ||
+ plugin.description.toLowerCase().includes(searchFieldValue.toLowerCase()) ||
+ plugin.author.toLowerCase().includes(searchFieldValue.toLowerCase()) ||
+ plugin.tags.some((tag: string) => tag.toLowerCase().includes(searchFieldValue.toLowerCase()))
+ );
+ })
+ .sort((a, b) => {
+ if (selectedSort % 2 === 1) return a.name.localeCompare(b.name);
+ else return b.name.localeCompare(a.name);
+ })
+ .map((plugin: StorePlugin) => (
+ <PluginCard plugin={plugin} />
+ ))}
+ </div>
+ </>
+ );
+};
+
+const AboutTab: FC<{}> = () => {
+ return (
+ <div
+ style={{
+ display: 'flex',
+ flexDirection: 'column',
+ }}
+ >
+ <style>{`
+ .deckyStoreAboutHeader {
+ font-size: 24px;
+ font-weight: 600;
+ margin-top: 20px;
+ }
+ `}</style>
+ <img
+ src={logo}
+ style={{
+ width: '256px',
+ height: 'auto',
+ alignSelf: 'center',
+ }}
+ />
+ <span className="deckyStoreAboutHeader">Testing</span>
+ <span>
+ Please consider testing new plugins to help the Decky Loader team!{' '}
+ <a
+ href="https://deckbrew.xyz/testing"
+ target="_blank"
+ style={{
+ textDecoration: 'none',
+ }}
+ >
+ deckbrew.xyz/testing
+ </a>
+ </span>
+ <span className="deckyStoreAboutHeader">Contributing</span>
+ <span>
+ If you would like to contribute to the Decky Plugin Store, check the SteamDeckHomebrew/decky-plugin-template
+ repository on GitHub. Information on development and distribution is available in the README.
+ </span>
+ <span className="deckyStoreAboutHeader">Source Code</span>
+ <span>All plugin source code is available on SteamDeckHomebrew/decky-plugin-database repository on GitHub.</span>
</div>
);
};
diff --git a/frontend/src/plugin-loader.tsx b/frontend/src/plugin-loader.tsx
index c37e168c..bcd84b3f 100644
--- a/frontend/src/plugin-loader.tsx
+++ b/frontend/src/plugin-loader.tsx
@@ -1,13 +1,4 @@
-import {
- ConfirmModal,
- ModalRoot,
- Patch,
- QuickAccessTab,
- Router,
- showModal,
- sleep,
- staticClasses,
-} from 'decky-frontend-lib';
+import { ConfirmModal, ModalRoot, Patch, QuickAccessTab, Router, showModal, sleep } from 'decky-frontend-lib';
import { FC, lazy } from 'react';
import { FaCog, FaExclamationCircle, FaPlug } from 'react-icons/fa';
@@ -155,10 +146,10 @@ class PluginLoader extends Logger {
onCancel={() => {
// do nothing
}}
+ strTitle={`Uninstall ${name}`}
+ strOKButtonText={'Uninstall'}
>
- <div className={staticClasses.Title} style={{ flexDirection: 'column' }}>
- Uninstall {name}?
- </div>
+ Are you sure you want to uninstall {name}?
</ConfirmModal>,
);
}
diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json
index 1e7159ee..6231d955 100644
--- a/frontend/tsconfig.json
+++ b/frontend/tsconfig.json
@@ -18,6 +18,6 @@
"allowSyntheticDefaultImports": true,
"skipLibCheck": true
},
- "include": ["src"],
+ "include": ["src", "index.d.ts"],
"exclude": ["node_modules"]
}