diff options
| author | EMERALD <hudson.samuels@gmail.com> | 2023-01-19 20:00:42 -0600 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-01-19 18:00:42 -0800 |
| commit | 3ebaac6752cb2b13ee5bfb6274cd7ae60b0d6bcb (patch) | |
| tree | 70b18b6f9ec2b2e23d2eef374e106a8870e794e8 | |
| parent | cbbd56486070eab9a08253b2778fcd64877acd68 (diff) | |
| download | decky-loader-3ebaac6752cb2b13ee5bfb6274cd7ae60b0d6bcb.tar.gz decky-loader-3ebaac6752cb2b13ee5bfb6274cd7ae60b0d6bcb.zip | |
Store and plugin installation visual improvements (#343)v2.5.0-pre1
* Redesign store, add comments for filtering
* Improve installation/uninstallation modals
* Fix store comment to be easier to fix
* Add source code info to about page
| -rw-r--r-- | frontend/assets/plugin_store.png | bin | 0 -> 56506 bytes | |||
| -rw-r--r-- | frontend/index.d.ts | 2 | ||||
| -rw-r--r-- | frontend/package.json | 3 | ||||
| -rw-r--r-- | frontend/pnpm-lock.yaml | 44 | ||||
| -rw-r--r-- | frontend/rollup.config.js | 2 | ||||
| -rw-r--r-- | frontend/src/components/modals/PluginInstallModal.tsx | 17 | ||||
| -rw-r--r-- | frontend/src/components/store/PluginCard.tsx | 277 | ||||
| -rw-r--r-- | frontend/src/components/store/Store.tsx | 224 | ||||
| -rw-r--r-- | frontend/src/plugin-loader.tsx | 17 | ||||
| -rw-r--r-- | frontend/tsconfig.json | 2 |
10 files changed, 397 insertions, 191 deletions
diff --git a/frontend/assets/plugin_store.png b/frontend/assets/plugin_store.png Binary files differnew file mode 100644 index 00000000..17832cab --- /dev/null +++ b/frontend/assets/plugin_store.png 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"] } |
