From 02edce29993f68ef734eab7b52349a1274221f7d Mon Sep 17 00:00:00 2001 From: bbedward Date: Fri, 30 Jan 2026 13:31:12 -0500 Subject: [PATCH] plugins: represent featured plugins in built-in browsers --- core/internal/plugins/registry.go | 1 + core/internal/plugins/search.go | 3 + core/internal/server/plugins/list.go | 1 + core/internal/server/plugins/types.go | 1 + .../Modals/Clipboard/ClipboardConstants.qml | 2 + .../Clipboard/ClipboardHistoryPopout.qml | 4 +- .../Modules/Settings/DesktopWidgetBrowser.qml | 63 +++++++++++++++---- quickshell/Modules/Settings/PluginBrowser.qml | 41 ++++++++++++ 8 files changed, 101 insertions(+), 15 deletions(-) diff --git a/core/internal/plugins/registry.go b/core/internal/plugins/registry.go index a3ba2be3..1728e723 100644 --- a/core/internal/plugins/registry.go +++ b/core/internal/plugins/registry.go @@ -27,6 +27,7 @@ type Plugin struct { Distro []string `json:"distro"` Screenshot string `json:"screenshot,omitempty"` RequiresDMS string `json:"requires_dms,omitempty"` + Featured bool `json:"featured,omitempty"` } type GitClient interface { diff --git a/core/internal/plugins/search.go b/core/internal/plugins/search.go index 01aa8846..833158f4 100644 --- a/core/internal/plugins/search.go +++ b/core/internal/plugins/search.go @@ -67,6 +67,9 @@ func FilterByCapability(capability string, plugins []Plugin) []Plugin { func SortByFirstParty(plugins []Plugin) []Plugin { sort.SliceStable(plugins, func(i, j int) bool { + if plugins[i].Featured != plugins[j].Featured { + return plugins[i].Featured + } isFirstPartyI := strings.HasPrefix(plugins[i].Repo, "https://github.com/AvengeMedia") isFirstPartyJ := strings.HasPrefix(plugins[j].Repo, "https://github.com/AvengeMedia") if isFirstPartyI != isFirstPartyJ { diff --git a/core/internal/server/plugins/list.go b/core/internal/server/plugins/list.go index 64e2faf8..e44d0342 100644 --- a/core/internal/server/plugins/list.go +++ b/core/internal/server/plugins/list.go @@ -44,6 +44,7 @@ func HandleList(conn net.Conn, req models.Request) { Dependencies: p.Dependencies, Installed: installed, FirstParty: strings.HasPrefix(p.Repo, "https://github.com/AvengeMedia"), + Featured: p.Featured, RequiresDMS: p.RequiresDMS, } } diff --git a/core/internal/server/plugins/types.go b/core/internal/server/plugins/types.go index 0586716c..8290f72a 100644 --- a/core/internal/server/plugins/types.go +++ b/core/internal/server/plugins/types.go @@ -13,6 +13,7 @@ type PluginInfo struct { Dependencies []string `json:"dependencies,omitempty"` Installed bool `json:"installed,omitempty"` FirstParty bool `json:"firstParty,omitempty"` + Featured bool `json:"featured,omitempty"` Note string `json:"note,omitempty"` HasUpdate bool `json:"hasUpdate,omitempty"` RequiresDMS string `json:"requires_dms,omitempty"` diff --git a/quickshell/Modals/Clipboard/ClipboardConstants.qml b/quickshell/Modals/Clipboard/ClipboardConstants.qml index 86f8bd4d..9434444f 100644 --- a/quickshell/Modals/Clipboard/ClipboardConstants.qml +++ b/quickshell/Modals/Clipboard/ClipboardConstants.qml @@ -9,6 +9,8 @@ Singleton { readonly property int longTextThreshold: 200 readonly property int modalWidth: 650 readonly property int modalHeight: 550 + readonly property int popoutWidth: 550 + readonly property int popoutHeight: 500 readonly property int itemHeight: 72 readonly property int thumbnailSize: 48 readonly property int retryInterval: 50 diff --git a/quickshell/Modals/Clipboard/ClipboardHistoryPopout.qml b/quickshell/Modals/Clipboard/ClipboardHistoryPopout.qml index d37893d7..7037b11f 100644 --- a/quickshell/Modals/Clipboard/ClipboardHistoryPopout.qml +++ b/quickshell/Modals/Clipboard/ClipboardHistoryPopout.qml @@ -111,8 +111,8 @@ DankPopout { return ClipboardService.getEntryType(entry); } - popupWidth: ClipboardConstants.modalWidth - popupHeight: ClipboardConstants.modalHeight + popupWidth: ClipboardConstants.popoutWidth + popupHeight: ClipboardConstants.popoutHeight triggerWidth: 55 positioning: "" screen: triggerScreen diff --git a/quickshell/Modules/Settings/DesktopWidgetBrowser.qml b/quickshell/Modules/Settings/DesktopWidgetBrowser.qml index 9680b953..7de37f46 100644 --- a/quickshell/Modules/Settings/DesktopWidgetBrowser.qml +++ b/quickshell/Modules/Settings/DesktopWidgetBrowser.qml @@ -19,24 +19,29 @@ FloatingWindow { function updateFilteredWidgets() { const allWidgets = DesktopWidgetRegistry.registeredWidgetsList || []; - if (!searchQuery || searchQuery.length === 0) { - filteredWidgets = allWidgets.slice(); - return; - } - var filtered = []; - var query = searchQuery.toLowerCase(); - for (var i = 0; i < allWidgets.length; i++) { - var widget = allWidgets[i]; - var name = widget.name ? widget.name.toLowerCase() : ""; - var description = widget.description ? widget.description.toLowerCase() : ""; - var id = widget.id ? widget.id.toLowerCase() : ""; + if (!searchQuery || searchQuery.length === 0) { + filtered = allWidgets.slice(); + } else { + var query = searchQuery.toLowerCase(); + for (var i = 0; i < allWidgets.length; i++) { + var widget = allWidgets[i]; + var name = widget.name ? widget.name.toLowerCase() : ""; + var description = widget.description ? widget.description.toLowerCase() : ""; + var id = widget.id ? widget.id.toLowerCase() : ""; - if (name.indexOf(query) !== -1 || description.indexOf(query) !== -1 || id.indexOf(query) !== -1) - filtered.push(widget); + if (name.indexOf(query) !== -1 || description.indexOf(query) !== -1 || id.indexOf(query) !== -1) + filtered.push(widget); + } } + filtered.sort((a, b) => { + if (a.featured !== b.featured) + return a.featured ? -1 : 1; + return 0; + }); + filteredWidgets = filtered; selectedIndex = -1; keyboardNavigationActive = false; @@ -356,6 +361,38 @@ FloatingWindow { color: Theme.surfaceText } + Rectangle { + visible: delegateRoot.modelData.featured || false + width: featuredWidgetRow.implicitWidth + Theme.spacingXS * 2 + height: 18 + radius: 9 + color: Theme.withAlpha(Theme.secondary, 0.15) + border.color: Theme.withAlpha(Theme.secondary, 0.4) + border.width: 1 + anchors.verticalCenter: parent.verticalCenter + + Row { + id: featuredWidgetRow + anchors.centerIn: parent + spacing: 2 + + DankIcon { + name: "star" + size: 10 + color: Theme.secondary + anchors.verticalCenter: parent.verticalCenter + } + + StyledText { + anchors.verticalCenter: parent.verticalCenter + text: I18n.tr("featured") + font.pixelSize: Theme.fontSizeSmall - 2 + color: Theme.secondary + font.weight: Font.Medium + } + } + } + Rectangle { visible: delegateRoot.modelData.type === "plugin" width: pluginLabel.implicitWidth + Theme.spacingXS * 2 diff --git a/quickshell/Modules/Settings/PluginBrowser.qml b/quickshell/Modules/Settings/PluginBrowser.qml index b87a9d80..db0b2d9f 100644 --- a/quickshell/Modules/Settings/PluginBrowser.qml +++ b/quickshell/Modules/Settings/PluginBrowser.qml @@ -48,6 +48,14 @@ FloatingWindow { filtered.push(plugin); } + filtered.sort((a, b) => { + if (a.featured !== b.featured) + return a.featured ? -1 : 1; + if (a.firstParty !== b.firstParty) + return a.firstParty ? -1 : 1; + return 0; + }); + filteredPlugins = filtered; selectedIndex = -1; keyboardNavigationActive = false; @@ -411,6 +419,7 @@ FloatingWindow { property bool isSelected: root.keyboardNavigationActive && index === root.selectedIndex property bool isInstalled: modelData.installed || false property bool isFirstParty: modelData.firstParty || false + property bool isFeatured: modelData.featured || false property bool isCompatible: PluginService.checkPluginCompatibility(modelData.requires_dms) color: isSelected ? Theme.primarySelected : Theme.withAlpha(Theme.surfaceVariant, 0.3) border.color: isSelected ? Theme.primary : Theme.withAlpha(Theme.outline, 0.2) @@ -449,6 +458,38 @@ FloatingWindow { anchors.verticalCenter: parent.verticalCenter } + Rectangle { + height: 16 + width: featuredRow.implicitWidth + Theme.spacingXS * 2 + radius: 8 + color: Theme.withAlpha(Theme.secondary, 0.15) + border.color: Theme.withAlpha(Theme.secondary, 0.4) + border.width: 1 + visible: isFeatured + anchors.verticalCenter: parent.verticalCenter + + Row { + id: featuredRow + anchors.centerIn: parent + spacing: 2 + + DankIcon { + name: "star" + size: 10 + color: Theme.secondary + anchors.verticalCenter: parent.verticalCenter + } + + StyledText { + text: I18n.tr("featured") + font.pixelSize: Theme.fontSizeSmall - 2 + color: Theme.secondary + font.weight: Font.Medium + anchors.verticalCenter: parent.verticalCenter + } + } + } + Rectangle { height: 16 width: firstPartyText.implicitWidth + Theme.spacingXS * 2