Skip to content

Commit 0a1c2f0

Browse files
committed
feat: add filter functionality and upgrade indicators for plugins #4356 #build
- Added filter placeholder and "Not Installed" text in multiple language files. - Introduced `IsUpgradable` field in `PluginDto` and `PluginDetail` to indicate if a plugin can be upgraded. - Implemented logic to determine if a plugin is upgradable in the plugin store handler. - Enhanced the plugin settings view to include upgrade buttons for upgradable plugins. - Added filtering options for installed and uninstalled plugins, as well as for upgradable plugins in the settings controller. - Updated dropdown button to support custom filter hint text.
1 parent d567d36 commit 0a1c2f0

File tree

12 files changed

+588
-155
lines changed

12 files changed

+588
-155
lines changed

wox.core/resource/lang/en_US.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,11 @@
119119
"ui_query_hotkeys_silent": "Silent",
120120
"ui_query_hotkeys_silent_tooltip": "When selected, if the count of query result is 1, Wox will execute the query directly without showing the result list.\n\nThis is useful when you have a query that always return one result and you want to execute it directly.\nNote: This option is only available when the query result count is 1. Otherwise it will be ignored and notify a warning.",
121121
"ui_disabled": "Disabled",
122+
"ui_plugin_filter_disabled_only": "Disabled Only",
123+
"ui_plugin_filter_enabled_only": "Enabled Only",
124+
"ui_plugin_filter_upgradable": "Upgradable",
125+
"ui_filter_placeholder": "Filter...",
126+
"ui_not_installed": "Not Installed",
122127
"ui_disabled_tooltip": "When selected, this item is temporarily disabled without being deleted.",
123128
"ui_add": "Add",
124129
"ui_query_shortcuts_shortcut": "Shortcut",

wox.core/resource/lang/pt_BR.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,11 @@
116116
"ui_query_hotkeys_silent": "Silencioso",
117117
"ui_query_hotkeys_silent_tooltip": "Quando selecionado, se o número de resultados da consulta for 1, o Wox executar a consulta diretamente sem mostrar a lista de resultados.\n\nIssotil quando você tem uma consulta que sempre retorna um resultado e deseja execut-la diretamente.\nNota: Esta opção s est disponível quando o número de resultados da consulta 1. Caso contrário, ser ignorado e notificar um aviso.",
118118
"ui_disabled": "Desativado",
119+
"ui_plugin_filter_disabled_only": "Somente desativados",
120+
"ui_plugin_filter_enabled_only": "Somente ativados",
121+
"ui_plugin_filter_upgradable": "Atualizáveis",
122+
"ui_filter_placeholder": "Filtrar...",
123+
"ui_not_installed": "Não instalado",
119124
"ui_disabled_tooltip": "Quando selecionado, este item é desativado temporariamente sem ser removido.",
120125
"ui_add": "Adicionar",
121126
"ui_query_shortcuts_shortcut": "Atalho",

wox.core/resource/lang/ru_RU.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,11 @@
116116
"ui_query_hotkeys_silent": "Сильный",
117117
"ui_query_hotkeys_silent_tooltip": "Если выбран, и количество результатов запроса равно 1, Wox выполнит запрос непосредственно без отображения списка результатов.\n\nЭто полезно, когда у вас есть запрос, который всегда возвращает один результат, и вы хотите выполнить его непосредственно.\nПримечание: Эта опция доступна только в том случае, если количество результатов запроса равно 1. В противном случае она будет проигнорирована и будет выведено предупреждение.",
118118
"ui_disabled": "Отключено",
119+
"ui_plugin_filter_disabled_only": "Только отключенные",
120+
"ui_plugin_filter_enabled_only": "Только включенные",
121+
"ui_plugin_filter_upgradable": "Доступно обновление",
122+
"ui_filter_placeholder": "Фильтр...",
123+
"ui_not_installed": "Не установлено",
119124
"ui_disabled_tooltip": "Если выбрано, этот пункт временно отключается без удаления.",
120125
"ui_add": "Добавить",
121126
"ui_query_shortcuts_shortcut": "Горячая клавиша",

wox.core/resource/lang/zh_CN.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,11 @@
119119
"ui_query_hotkeys_silent": "静默",
120120
"ui_query_hotkeys_silent_tooltip": "当选中时,如果查询结果数量为1,Wox将直接执行查询而不显示结果列表。\n\n这在您有一个总是返回一个结果的查询并且您希望直接执行它时非常有用。\n注意:此选项仅在查询结果数量为1时可用。否则将忽略并通知警告。",
121121
"ui_disabled": "禁用",
122+
"ui_plugin_filter_disabled_only": "仅禁用",
123+
"ui_plugin_filter_enabled_only": "仅启用",
124+
"ui_plugin_filter_upgradable": "可升级",
125+
"ui_filter_placeholder": "筛选...",
126+
"ui_not_installed": "未安装",
122127
"ui_disabled_tooltip": "选中后将临时禁用该项,但不会删除。",
123128
"ui_add": "添加",
124129
"ui_query_shortcuts_shortcut": "快捷键",

wox.core/ui/dto/plugin_dto.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ type PluginDto struct {
3131
IsDev bool
3232
IsInstalled bool
3333
IsDisable bool // only available when plugin is installed
34+
IsUpgradable bool
3435
}
3536

3637
type PluginSettingDto struct {

wox.core/ui/router.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"wox/util/hotkey"
2929
"wox/util/shell"
3030

31+
"github.com/Masterminds/semver/v3"
3132
"github.com/jinzhu/copier"
3233
"github.com/samber/lo"
3334
"github.com/tidwall/gjson"
@@ -192,6 +193,10 @@ func handlePluginStore(w http.ResponseWriter, r *http.Request) {
192193
plugins[i].Icon = common.NewWoxImageUrl(manifests[i].IconUrl)
193194
}
194195
plugins[i].IsInstalled = isInstalled
196+
plugins[i].IsUpgradable = false
197+
if isInstalled {
198+
plugins[i].IsUpgradable = isPluginUpgradable(pluginInstance.Metadata.Version, manifests[i].Version)
199+
}
195200
plugins[i].Name = manifests[i].GetName(getCtx)
196201
plugins[i].NameEn = manifests[i].GetNameEn(getCtx)
197202
plugins[i].Description = manifests[i].GetDescription(getCtx)
@@ -242,8 +247,10 @@ func convertPluginInstanceToDto(ctx context.Context, pluginInstance *plugin.Inst
242247
storePlugin, foundErr := plugin.GetStoreManager().GetStorePluginManifestById(ctx, pluginInstance.Metadata.Id)
243248
if foundErr == nil {
244249
installedPlugin.ScreenshotUrls = storePlugin.ScreenshotUrls
250+
installedPlugin.IsUpgradable = isPluginUpgradable(pluginInstance.Metadata.Version, storePlugin.Version)
245251
} else {
246252
installedPlugin.ScreenshotUrls = []string{}
253+
installedPlugin.IsUpgradable = false
247254
}
248255

249256
// load icon
@@ -260,6 +267,16 @@ func convertPluginInstanceToDto(ctx context.Context, pluginInstance *plugin.Inst
260267
return installedPlugin, nil
261268
}
262269

270+
func isPluginUpgradable(installedVersion string, storeVersion string) bool {
271+
installed, installedErr := semver.NewVersion(installedVersion)
272+
store, storeErr := semver.NewVersion(storeVersion)
273+
if installedErr != nil || storeErr != nil || installed == nil || store == nil {
274+
return false
275+
}
276+
277+
return store.GreaterThan(installed)
278+
}
279+
263280
func handlePluginInstall(w http.ResponseWriter, r *http.Request) {
264281
ctx := getTraceContext(r)
265282

wox.ui.flutter/wox/lib/components/wox_ai_model_selector_view.dart

Lines changed: 45 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,7 @@ class WoxAIModelSelectorView extends StatefulWidget {
2222
/// Whether to allow editing the model
2323
final bool allowEdit;
2424

25-
const WoxAIModelSelectorView({
26-
super.key,
27-
this.initialValue,
28-
required this.onModelSelected,
29-
this.allowEdit = true,
30-
});
25+
const WoxAIModelSelectorView({super.key, this.initialValue, required this.onModelSelected, this.allowEdit = true});
3126

3227
@override
3328
State<WoxAIModelSelectorView> createState() => _WoxAIModelSelectorViewState();
@@ -143,32 +138,19 @@ class _WoxAIModelSelectorViewState extends State<WoxAIModelSelectorView> {
143138
final bg = getThemeActiveBackgroundColor().withAlpha(70);
144139
return Container(
145140
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
146-
decoration: BoxDecoration(
147-
color: bg,
148-
borderRadius: BorderRadius.circular(6),
149-
border: Border.all(color: getThemeActiveBackgroundColor().withAlpha(90)),
150-
),
141+
decoration: BoxDecoration(color: bg, borderRadius: BorderRadius.circular(6), border: Border.all(color: getThemeActiveBackgroundColor().withAlpha(90))),
151142
child: Row(
152143
crossAxisAlignment: CrossAxisAlignment.center,
153144
children: [
154-
const Padding(
155-
padding: EdgeInsets.only(top: 2.0),
156-
child: Icon(Icons.info, size: 14),
157-
),
145+
const Padding(padding: EdgeInsets.only(top: 2.0), child: Icon(Icons.info, size: 14)),
158146
const SizedBox(width: 10),
159147
Expanded(
160148
child: Column(
161149
crossAxisAlignment: CrossAxisAlignment.start,
162150
children: [
163-
Text(
164-
tr('ui_ai_model_selector_no_models_title'),
165-
style: TextStyle(color: getThemeTextColor(), fontWeight: FontWeight.w600),
166-
),
151+
Text(tr('ui_ai_model_selector_no_models_title'), style: TextStyle(color: getThemeTextColor(), fontWeight: FontWeight.w600)),
167152
const SizedBox(height: 4),
168-
Text(
169-
tr('ui_ai_model_selector_no_models_desc'),
170-
style: TextStyle(color: getThemeTextColor()),
171-
),
153+
Text(tr('ui_ai_model_selector_no_models_desc'), style: TextStyle(color: getThemeTextColor())),
172154
],
173155
),
174156
),
@@ -179,7 +161,7 @@ class _WoxAIModelSelectorViewState extends State<WoxAIModelSelectorView> {
179161
// Switch to the AI settings page within the settings view
180162
Get.find<WoxSettingController>().activeNavPath.value = 'ai';
181163
},
182-
)
164+
),
183165
],
184166
),
185167
);
@@ -210,12 +192,7 @@ class _WoxAIModelSelectorViewState extends State<WoxAIModelSelectorView> {
210192
child: WoxDropdownButton<String>(
211193
value: selectedProviderKey,
212194
isExpanded: true,
213-
items: providerKeys
214-
.map((providerKey) => WoxDropdownItem<String>(
215-
value: providerKey,
216-
label: getProviderLabel(providerKey),
217-
))
218-
.toList(),
195+
items: providerKeys.map((providerKey) => WoxDropdownItem<String>(value: providerKey, label: getProviderLabel(providerKey))).toList(),
219196
onChanged: (providerKey) {
220197
if (providerKey != null) {
221198
setState(() {
@@ -241,58 +218,47 @@ class _WoxAIModelSelectorViewState extends State<WoxAIModelSelectorView> {
241218
// Model selector or editor
242219
Expanded(
243220
flex: 2,
244-
child: isEditMode
245-
? WoxTextField(
246-
controller: nameController,
247-
hintText: tr('ui_ai_model_selector_model_name'),
248-
width: double.infinity, // fill the Expanded width
249-
onChanged: (value) {
250-
if (value.isNotEmpty && selectedProviderKey != null) {
251-
final providerInfo = parseProviderKey(selectedProviderKey!);
252-
final updatedModel = AIModel(
253-
name: value,
254-
provider: providerInfo.$1,
255-
providerAlias: providerInfo.$2,
256-
);
257-
setState(() => this.selectedModel = updatedModel);
258-
widget.onModelSelected(jsonEncode(updatedModel));
259-
}
260-
},
261-
)
262-
: WoxDropdownButton<String>(
263-
value: selectedModel?.name,
264-
isExpanded: true,
265-
enableFilter: true,
266-
items: getProviderModels()
267-
.map((model) => WoxDropdownItem<String>(
268-
value: model.name,
269-
label: model.name,
270-
))
271-
.toList(),
272-
onChanged: (modelName) {
273-
if (modelName != null && selectedProviderKey != null) {
274-
final providerInfo = parseProviderKey(selectedProviderKey!);
275-
final selectedModel = allModels.firstWhere(
276-
(m) => m.provider == providerInfo.$1 && m.providerAlias == providerInfo.$2 && m.name == modelName,
277-
orElse: () => AIModel(
278-
name: modelName,
279-
provider: providerInfo.$1,
280-
providerAlias: providerInfo.$2,
281-
),
282-
);
283-
284-
setState(() {
285-
this.selectedModel = selectedModel;
286-
nameController.text = selectedModel.name;
287-
});
288-
widget.onModelSelected(jsonEncode(selectedModel));
289-
}
290-
},
291-
),
221+
child:
222+
isEditMode
223+
? WoxTextField(
224+
controller: nameController,
225+
hintText: tr('ui_ai_model_selector_model_name'),
226+
width: double.infinity, // fill the Expanded width
227+
onChanged: (value) {
228+
if (value.isNotEmpty && selectedProviderKey != null) {
229+
final providerInfo = parseProviderKey(selectedProviderKey!);
230+
final updatedModel = AIModel(name: value, provider: providerInfo.$1, providerAlias: providerInfo.$2);
231+
setState(() => selectedModel = updatedModel);
232+
widget.onModelSelected(jsonEncode(updatedModel));
233+
}
234+
},
235+
)
236+
: WoxDropdownButton<String>(
237+
value: selectedModel?.name,
238+
isExpanded: true,
239+
enableFilter: true,
240+
filterHintText: tr('ui_filter_placeholder'),
241+
items: getProviderModels().map((model) => WoxDropdownItem<String>(value: model.name, label: model.name)).toList(),
242+
onChanged: (modelName) {
243+
if (modelName != null && selectedProviderKey != null) {
244+
final providerInfo = parseProviderKey(selectedProviderKey!);
245+
final selectedModel = allModels.firstWhere(
246+
(m) => m.provider == providerInfo.$1 && m.providerAlias == providerInfo.$2 && m.name == modelName,
247+
orElse: () => AIModel(name: modelName, provider: providerInfo.$1, providerAlias: providerInfo.$2),
248+
);
249+
250+
setState(() {
251+
this.selectedModel = selectedModel;
252+
nameController.text = selectedModel.name;
253+
});
254+
widget.onModelSelected(jsonEncode(selectedModel));
255+
}
256+
},
257+
),
292258
),
293259

294260
// Toggle edit/select mode button
295-
if (widget.allowEdit && this.selectedModel != null)
261+
if (widget.allowEdit && selectedModel != null)
296262
IconButton(
297263
icon: Icon(isEditMode ? Icons.list : Icons.edit, color: getThemeTextColor()),
298264
onPressed: () {

wox.ui.flutter/wox/lib/components/wox_dropdown_button.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ class WoxDropdownButton<T> extends StatefulWidget {
3232
final double? width;
3333
final Widget? underline;
3434
final bool enableFilter;
35+
final String? filterHintText;
3536
final FocusNode? focusNode;
3637
final bool autofocus;
3738
final bool multiSelect;
@@ -55,6 +56,7 @@ class WoxDropdownButton<T> extends StatefulWidget {
5556
this.width,
5657
this.underline,
5758
this.enableFilter = false,
59+
this.filterHintText,
5860
this.focusNode,
5961
this.autofocus = false,
6062
this.multiSelect = false,
@@ -316,7 +318,7 @@ class _WoxDropdownButtonState<T> extends State<WoxDropdownButton<T>> {
316318
textAlignVertical: TextAlignVertical.center,
317319
style: TextStyle(color: searchTextColor, fontSize: widget.fontSize).useSystemChineseFont(),
318320
decoration: InputDecoration(
319-
hintText: 'Filter...',
321+
hintText: widget.filterHintText ?? 'Filter...',
320322
hintStyle: TextStyle(color: searchHintColor, fontSize: widget.fontSize).useSystemChineseFont(),
321323
border: InputBorder.none,
322324
isDense: true,

0 commit comments

Comments
 (0)