Merge remote-tracking branch 'upstream/main' into HEAD

This commit is contained in:
xXJsonDeruloXx committed 2026-10-08 07:08:45 -04:00
commit fab65e4ca4
8 files changed
+85 -24

No files matched your search

+3
View File
@@ -217,6 +217,9 @@
"developer_mode": {
"label": "Developer mode"
},
"sort_plugins": {
"label": "Sort plugins alphabetically"
},
"notifications": {
"decky_updates_label": "Decky update available",
"header": "Notifications",
+17 -13
View File
@@ -333,20 +333,24 @@ class Utilities:
#Resolving all files/folders in the requested directory
for file in path_obj.iterdir():
if file.exists():
filest = file.stat()
is_hidden = file.name.startswith('.')
if ON_WINDOWS and not is_hidden:
is_hidden = bool(filest.st_file_attributes & FILE_ATTRIBUTE_HIDDEN) # type: ignore
if include_folders and file.is_dir():
if (is_hidden and include_hidden) or not is_hidden:
folders.append({"file": file, "filest": filest, "is_dir": True})
elif include_files:
# Handle requested extensions if present
if include_ext == None or len(include_ext) == 0 or 'all_files' in include_ext \
or splitext(file.name)[1].lstrip('.').upper() in (ext.upper() for ext in include_ext):
try:
if file.exists():
filest = file.stat()
is_hidden = file.name.startswith('.')
if ON_WINDOWS and not is_hidden:
is_hidden = bool(filest.st_file_attributes & FILE_ATTRIBUTE_HIDDEN) # type: ignore
if include_folders and file.is_dir():
if (is_hidden and include_hidden) or not is_hidden:
files.append({"file": file, "filest": filest, "is_dir": False})
folders.append({"file": file, "filest": filest, "is_dir": True})
elif include_files:
# Handle requested extensions if present
if include_ext == None or len(include_ext) == 0 or 'all_files' in include_ext \
or splitext(file.name)[1].lstrip('.').upper() in (ext.upper() for ext in include_ext):
if (is_hidden and include_hidden) or not is_hidden:
files.append({"file": file, "filest": filest, "is_dir": False})
except (PermissionError, OSError):
self.logger.debug(f"Skipping inaccessible file picker entry: {file}")
continue
# Filter logic
if filter_for is not None:
try:
+1 -1
View File
@@ -9,7 +9,7 @@ a = Analysis(
('decky_loader/locales', 'decky_loader/locales'),
('decky_loader/static', 'decky_loader/static'),
] + copy_metadata('decky_loader'),
hiddenimports=['logging.handlers', 'sqlite3', 'decky_plugin', 'decky'],
hiddenimports=['logging.handlers', 'sqlite3', 'http.server', 'socketserver', 'configparser', 'decky_plugin', 'decky'],
)
pyz = PYZ(a.pure, a.zipped_data)
+11
View File
@@ -10,6 +10,7 @@ interface PublicDeckyState {
disabledPlugins: DisabledPlugin[];
installedPlugins: (Plugin | DisabledPlugin)[];
pluginOrder: string[];
sortPlugins: boolean;
frozenPlugins: string[];
hiddenPlugins: string[];
activePlugin: Plugin | null;
@@ -31,6 +32,7 @@ export class DeckyState {
private _disabledPlugins: DisabledPlugin[] = [];
private _installedPlugins: (Plugin | DisabledPlugin)[] = [];
private _pluginOrder: string[] = [];
private _sortPlugins: boolean = false;
private _frozenPlugins: string[] = [];
private _hiddenPlugins: string[] = [];
private _activePlugin: Plugin | null = null;
@@ -49,6 +51,7 @@ export class DeckyState {
disabledPlugins: this._disabledPlugins,
installedPlugins: this._installedPlugins,
pluginOrder: this._pluginOrder,
sortPlugins: this._sortPlugins,
frozenPlugins: this._frozenPlugins,
hiddenPlugins: this._hiddenPlugins,
activePlugin: this._activePlugin,
@@ -83,6 +86,11 @@ export class DeckyState {
this.notifyUpdate();
}
setSortPlugins(sortPlugins: boolean) {
this._sortPlugins = sortPlugins;
this.notifyUpdate();
}
setFrozenPlugins(frozenPlugins: string[]) {
this._frozenPlugins = frozenPlugins;
this.notifyUpdate();
@@ -139,6 +147,7 @@ interface DeckyStateContext extends PublicDeckyState {
setActivePlugin(name: string): void;
setPluginOrder(pluginOrder: string[]): void;
setDisabledPlugins(disabled: DisabledPlugin[]): void;
setSortPlugins(sortPlugins: boolean): void;
closeActivePlugin(): void;
}
@@ -178,6 +187,7 @@ export const DeckyStateContextProvider: FC<Props> = ({ children, deckyState }) =
const closeActivePlugin = deckyState.closeActivePlugin.bind(deckyState);
const setPluginOrder = deckyState.setPluginOrder.bind(deckyState);
const setDisabledPlugins = deckyState.setDisabledPlugins.bind(deckyState);
const setSortPlugins = deckyState.setSortPlugins.bind(deckyState);
return (
<DeckyStateContext.Provider
@@ -189,6 +199,7 @@ export const DeckyStateContextProvider: FC<Props> = ({ children, deckyState }) =
closeActivePlugin,
setPluginOrder,
setDisabledPlugins,
setSortPlugins,
}}
>
{children}
+7 -2
View File
@@ -16,6 +16,7 @@ const PluginView: FC = () => {
updates,
activePlugin,
pluginOrder,
sortPlugins,
setActivePlugin,
closeActivePlugin,
} = useDeckyState();
@@ -26,10 +27,14 @@ const PluginView: FC = () => {
console.log('updating PluginView after changes');
return [...plugins]
.sort((a, b) => pluginOrder.indexOf(a.name) - pluginOrder.indexOf(b.name))
.sort((a, b) =>
sortPlugins
? a.name.localeCompare(b.name, undefined, { sensitivity: 'base' })
: pluginOrder.indexOf(a.name) - pluginOrder.indexOf(b.name),
)
.filter((p) => p.content)
.filter(({ name }) => !hiddenPlugins.includes(name));
}, [plugins, pluginOrder, hiddenPlugins]);
}, [plugins, pluginOrder, sortPlugins, hiddenPlugins]);
const numberOfHidden = hiddenPlugins.filter((name) => !!plugins.find((p) => p.name === name)).length;
@@ -1,6 +1,7 @@
import { DialogBody, DialogControlsSection, DialogControlsSectionHeader, Field, Toggle } from '@decky/ui';
import { useTranslation } from 'react-i18next';
import { useSetting } from '../../../../utils/hooks/useSetting';
import { useDeckyState } from '../../../DeckyState';
import BranchSelect from './BranchSelect';
import NotificationSettings from './NotificationSettings';
@@ -14,7 +15,8 @@ export default function GeneralSettings({
isDeveloper: boolean;
setIsDeveloper: (val: boolean) => void;
}) {
const { versionInfo } = useDeckyState();
const { versionInfo, sortPlugins, setSortPlugins } = useDeckyState();
const [_, setSortPluginsSetting] = useSetting<boolean>('sortPlugins', false);
const { t } = useTranslation();
return (
@@ -42,6 +44,15 @@ export default function GeneralSettings({
}}
/>
</Field>
<Field label={t('SettingsGeneralIndex.sort_plugins.label')}>
<Toggle
value={sortPlugins}
onChange={(toggleValue) => {
setSortPlugins(toggleValue);
setSortPluginsSetting(toggleValue);
}}
/>
</Field>
</DialogControlsSection>
<DialogControlsSection>
<DialogControlsSectionHeader>{t('SettingsGeneralIndex.about.header')}</DialogControlsSectionHeader>
@@ -11,7 +11,7 @@ import {
ReorderableList,
showContextMenu,
} from '@decky/ui';
import { useEffect, useState } from 'react';
import { useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { FaDownload, FaEllipsisH, FaRecycle } from 'react-icons/fa';
@@ -171,9 +171,16 @@ type PluginData = {
};
export default function PluginList({ isDeveloper }: { isDeveloper: boolean }) {
const { installedPlugins, disabledPlugins, updates, pluginOrder, setPluginOrder, frozenPlugins, hiddenPlugins } =
useDeckyState();
const {
installedPlugins,
disabledPlugins,
updates,
pluginOrder,
setPluginOrder,
frozenPlugins,
hiddenPlugins,
sortPlugins,
} = useDeckyState();
const [_, setPluginOrderSetting] = useSetting<string[]>(
'pluginOrder',
installedPlugins.map((plugin) => plugin.name),
@@ -188,6 +195,12 @@ export default function PluginList({ isDeveloper }: { isDeveloper: boolean }) {
const hiddenPluginsService = DeckyPluginLoader.hiddenPluginsService;
const frozenPluginsService = DeckyPluginLoader.frozenPluginsService;
const sortedPlugins = useMemo(() => {
return sortPlugins
? [...installedPlugins].sort((a, b) => a.name.localeCompare(b.name, undefined, { sensitivity: 'base' }))
: installedPlugins;
}, [installedPlugins, sortPlugins]);
useEffect(() => {
setPluginEntries(
installedPlugins.map(({ name, version }) => {
@@ -204,7 +217,7 @@ export default function PluginList({ isDeveloper }: { isDeveloper: boolean }) {
disabled={disabledPlugins.find((p) => p.name == name) !== undefined}
/>
),
position: pluginOrder.indexOf(name),
position: sortPlugins ? sortedPlugins.findIndex((p) => p.name === name) : pluginOrder.indexOf(name),
data: {
name,
disabled: disabledPlugins.some((disabledPlugin) => disabledPlugin.name === name),
@@ -221,7 +234,7 @@ export default function PluginList({ isDeveloper }: { isDeveloper: boolean }) {
};
}),
);
}, [installedPlugins, updates, hiddenPlugins, disabledPlugins]);
}, [installedPlugins, updates, hiddenPlugins, disabledPlugins, sortPlugins, sortedPlugins]);
if (installedPlugins.length === 0) {
return (
@@ -232,6 +245,9 @@ export default function PluginList({ isDeveloper }: { isDeveloper: boolean }) {
}
function onSave(entries: ReorderableEntry<PluginTableData>[]) {
if (sortPlugins) {
return;
}
const newOrder = entries.map((entry) => entry.data!.name);
console.log(newOrder);
setPluginOrder(newOrder);
@@ -265,7 +281,12 @@ export default function PluginList({ isDeveloper }: { isDeveloper: boolean }) {
</DialogButton>
)}
<DialogControlsSection style={{ marginTop: 0 }}>
<ReorderableList<PluginTableData> entries={pluginEntries} onSave={onSave} interactables={PluginInteractables} />
<ReorderableList<PluginTableData>
entries={pluginEntries}
onSave={onSave}
interactables={PluginInteractables}
disableReordering={sortPlugins}
/>
</DialogControlsSection>
</DialogBody>
);
+6
View File
@@ -360,6 +360,12 @@ class PluginLoader extends Logger {
this.deckyState.setPluginOrder(pluginOrder);
});
// Grab and set sort plugins setting
getSetting<boolean>('sortPlugins', false).then((sortPlugins) => {
this.debug('sortPlugins: ', sortPlugins);
this.deckyState.setSortPlugins(sortPlugins);
});
this.frozenPluginsService.init();
this.hiddenPluginsService.init();
this.notificationService.init();