feat: add search and filtering to batch installer dialog

This commit is contained in:
fujie
2026-03-16 14:33:06 +08:00
parent 7a2ecee010
commit 5a7b1d5097
11 changed files with 122 additions and 56 deletions

View File

@@ -10,7 +10,7 @@ One-click batch install plugins from GitHub repositories to your OpenWebUI insta
- **Auto-Update**: Automatically updates previously installed plugins
- **Public GitHub Support**: Install plugins from any public GitHub repository
- **Multi-Type Support**: Supports Pipe, Action, Filter, and Tool plugins
- **Interactive Selection Dialog**: Review the filtered list with type tags and plugin descriptions, then install only the checked subset
- **Interactive Selection Dialog**: Filter by type, search by keyword, review plugin descriptions, then install only the checked subset
- **i18n**: Supports 11 languages
## Flow
@@ -33,7 +33,7 @@ User Input
┌─────────────────────────────────────┐
│ Show Selection Dialog │
│ (checkbox list + type tags + desc) │
│ (type filter + search + desc)
└─────────────────────────────────────┘
├── [Cancel] → End
@@ -59,7 +59,7 @@ User Input
Each request handles one repository. To mix repositories, send another request after the previous installation completes.
After plugin discovery and filtering, OpenWebUI opens a browser dialog built with the `execute` event so you can review plugin descriptions, use type tags for quick batch selection, and check exactly which plugins to install before the API calls start.
After plugin discovery and filtering, OpenWebUI opens a browser dialog built with the `execute` event so you can filter by plugin type, search by keyword, review plugin descriptions, and check exactly which plugins to install before the API calls start.
## Quick Start: Install Popular Collections

View File

@@ -10,7 +10,7 @@
- 自动更新:自动更新之前安装过的插件
- 公开 GitHub 支持:支持从任何公开 GitHub 仓库安装插件
- 多类型支持:支持 Pipe、Action、Filter 和 Tool 插件
- 交互式选择对话框:先查看带类型标签和描述信息的过滤列表,再勾选要安装的插件,只安装所选子集
- 交互式选择对话框:先按类型筛选、按关键词搜索并查看描述信息,再勾选要安装的插件,只安装所选子集
- 国际化:支持 11 种语言
## 流程
@@ -33,7 +33,7 @@
┌─────────────────────────────────────┐
│ 显示选择对话框 │
│ (复选列表 + 类型标签 + 描述) │
│ (类型筛选 + 搜索 + 描述)
└─────────────────────────────────────┘
├── [取消] → 结束
@@ -59,7 +59,7 @@
每次请求处理一个仓库。如需混合多个来源,请在上一次安装完成后再发起下一次请求。
在插件发现和过滤完成后OpenWebUI 会通过 `execute` 事件打开浏览器选择对话框。你可以先查看插件描述,使用类型标签进行批量勾选,再开始调用安装 API。
在插件发现和过滤完成后OpenWebUI 会通过 `execute` 事件打开浏览器选择对话框。你可以先按类型筛选、通过关键词搜索、查看插件描述,再开始调用安装 API。
## 快速开始:安装热门插件集

View File

@@ -14,7 +14,7 @@ One-click batch install plugins from GitHub repositories to your OpenWebUI insta
- **Auto-Update**: Automatically updates previously installed plugins
- **Public GitHub Support**: Install plugins from any public GitHub repository
- **Multi-Type Support**: Supports Pipe, Action, Filter, and Tool plugins
- **Interactive Selection Dialog**: Review the filtered list with type tags and plugin descriptions, then install only the checked subset
- **Interactive Selection Dialog**: Filter by type, search by keyword, review plugin descriptions, then install only the checked subset
- **i18n**: Supports 11 languages
## Flow
@@ -37,7 +37,7 @@ User Input
┌─────────────────────────────────────┐
│ Show Selection Dialog │
│ (checkbox list + type tags + desc) │
│ (type filter + search + desc)
└─────────────────────────────────────┘
├── [Cancel] → End
@@ -63,7 +63,7 @@ User Input
Each request handles one repository. To mix repositories, send another request after the previous installation completes.
After plugin discovery and filtering, OpenWebUI opens a browser dialog built with the `execute` event so you can review plugin descriptions, use type tags for quick batch selection, and check exactly which plugins to install before the API calls start.
After plugin discovery and filtering, OpenWebUI opens a browser dialog built with the `execute` event so you can filter by plugin type, search by keyword, review plugin descriptions, and check exactly which plugins to install before the API calls start.
## Quick Start: Install Popular Collections

View File

@@ -14,7 +14,7 @@
- 自动更新:自动更新之前安装过的插件
- 公开 GitHub 支持:支持从任何公开 GitHub 仓库安装插件
- 多类型支持:支持 Pipe、Action、Filter 和 Tool 插件
- 交互式选择对话框:先查看带类型标签和描述信息的过滤列表,再勾选要安装的插件,只安装所选子集
- 交互式选择对话框:先按类型筛选、按关键词搜索并查看描述信息,再勾选要安装的插件,只安装所选子集
- 国际化:支持 11 种语言
## 流程
@@ -37,7 +37,7 @@
┌─────────────────────────────────────┐
│ 显示选择对话框 │
│ (复选列表 + 类型标签 + 描述) │
│ (类型筛选 + 搜索 + 描述)
└─────────────────────────────────────┘
├── [取消] → 结束
@@ -63,7 +63,7 @@
每次请求处理一个仓库。如需混合多个来源,请在上一次安装完成后再发起下一次请求。
在插件发现和过滤完成后OpenWebUI 会通过 `execute` 事件打开浏览器选择对话框。你可以先查看插件描述,使用类型标签进行批量勾选,再开始调用安装 API。
在插件发现和过滤完成后OpenWebUI 会通过 `execute` 事件打开浏览器选择对话框。你可以先按类型筛选、通过关键词搜索、查看插件描述,再开始调用安装 API。
## 快速开始:安装热门插件集

View File

@@ -10,7 +10,7 @@ Installing plugins in OpenWebUI should not feel like an all-or-nothing jump. Wit
### 🚀 Interactive Plugin Selection
- Uses the OpenWebUI `execute` event to open a custom browser dialog
- Displays the filtered plugin list with checkboxes, type tags, plugin descriptions, and repository context
- Displays the filtered plugin list with checkboxes, type filters, keyword search, plugin descriptions, and repository context
- Installs only the plugins the user keeps selected
### ✅ Smart Safety Features

View File

@@ -10,7 +10,7 @@
### 🚀 交互式插件选择
- 基于 OpenWebUI 的 `execute` 事件打开自定义浏览器选择对话框
- 显示带复选框、类型标签、插件描述和仓库信息的过滤结果
- 显示带复选框、类型筛选、关键词搜索、插件描述和仓库信息的过滤结果
- 只安装用户保留勾选的插件
### ✅ 智能安全保障

View File

@@ -14,7 +14,7 @@ One-click batch install plugins from GitHub repositories to your OpenWebUI insta
- **Auto-Update**: Automatically updates previously installed plugins
- **Public GitHub Support**: Install plugins from any public GitHub repository
- **Multi-Type Support**: Supports Pipe, Action, Filter, and Tool plugins
- **Interactive Selection Dialog**: Review the filtered list with type tags and plugin descriptions, then install only the checked subset
- **Interactive Selection Dialog**: Filter by type, search by keyword, review plugin descriptions, then install only the checked subset
- **i18n**: Supports 11 languages
## Flow
@@ -37,7 +37,7 @@ User Input
┌─────────────────────────────────────┐
│ Show Selection Dialog │
│ (checkbox list + type tags + desc) │
│ (type filter + search + desc)
└─────────────────────────────────────┘
├── [Cancel] → End
@@ -63,7 +63,7 @@ User Input
Each request handles one repository. To mix repositories, send another request after the previous installation completes.
After plugin discovery and filtering, OpenWebUI opens a browser dialog built with the `execute` event so you can review plugin descriptions, use type tags for quick batch selection, and check exactly which plugins to install before the API calls start.
After plugin discovery and filtering, OpenWebUI opens a browser dialog built with the `execute` event so you can filter by plugin type, search by keyword, review plugin descriptions, and check exactly which plugins to install before the API calls start.
## Quick Start: Install Popular Collections

View File

@@ -14,7 +14,7 @@
- 自动更新:自动更新之前安装过的插件
- 公开 GitHub 支持:支持从任何公开 GitHub 仓库安装插件
- 多类型支持:支持 Pipe、Action、Filter 和 Tool 插件
- 交互式选择对话框:先查看带类型标签和描述信息的过滤列表,再勾选要安装的插件,只安装所选子集
- 交互式选择对话框:先按类型筛选、按关键词搜索并查看描述信息,再勾选要安装的插件,只安装所选子集
- 国际化:支持 11 种语言
## 流程
@@ -37,7 +37,7 @@
┌─────────────────────────────────────┐
│ 显示选择对话框 │
│ (复选列表 + 类型标签 + 描述) │
│ (类型筛选 + 搜索 + 描述)
└─────────────────────────────────────┘
├── [取消] → 结束
@@ -63,7 +63,7 @@
每次请求处理一个仓库。如需混合多个来源,请在上一次安装完成后再发起下一次请求。
在插件发现和过滤完成后OpenWebUI 会通过 `execute` 事件打开浏览器选择对话框。你可以先查看插件描述,使用类型标签进行批量勾选,再开始调用安装 API。
在插件发现和过滤完成后OpenWebUI 会通过 `execute` 事件打开浏览器选择对话框。你可以先按类型筛选、通过关键词搜索、查看插件描述,再开始调用安装 API。
## 快速开始:安装热门插件集

View File

@@ -303,7 +303,11 @@ SELECTION_DIALOG_TEXTS = {
"en-US": {
"select_all": "Select all",
"clear_all": "Clear all",
"quick_select": "Types",
"quick_select": "Filter by type",
"all_types": "All",
"search_label": "Search",
"search_placeholder": "Search title, description, file path...",
"no_results": "No plugins match the current filter.",
"selected_count": "{count} selected",
"install_selected": "Install Selected",
"cancel": "Cancel",
@@ -315,7 +319,11 @@ SELECTION_DIALOG_TEXTS = {
"zh-CN": {
"select_all": "全选",
"clear_all": "清空",
"quick_select": "类型标签",
"quick_select": "类型筛选",
"all_types": "全部",
"search_label": "搜索",
"search_placeholder": "搜索标题、描述、文件路径...",
"no_results": "当前筛选条件下没有匹配的插件。",
"selected_count": "已选 {count}",
"install_selected": "安装所选插件",
"cancel": "取消",
@@ -327,7 +335,11 @@ SELECTION_DIALOG_TEXTS = {
"zh-HK": {
"select_all": "全選",
"clear_all": "清空",
"quick_select": "類型標籤",
"quick_select": "類型篩選",
"all_types": "全部",
"search_label": "搜尋",
"search_placeholder": "搜尋標題、描述、檔案路徑...",
"no_results": "目前篩選條件下沒有相符的外掛。",
"selected_count": "已選 {count}",
"install_selected": "安裝所選外掛",
"cancel": "取消",
@@ -339,7 +351,11 @@ SELECTION_DIALOG_TEXTS = {
"zh-TW": {
"select_all": "全選",
"clear_all": "清空",
"quick_select": "類型標籤",
"quick_select": "類型篩選",
"all_types": "全部",
"search_label": "搜尋",
"search_placeholder": "搜尋標題、描述、檔案路徑...",
"no_results": "目前篩選條件下沒有符合的外掛。",
"selected_count": "已選 {count}",
"install_selected": "安裝所選外掛",
"cancel": "取消",
@@ -351,7 +367,11 @@ SELECTION_DIALOG_TEXTS = {
"ko-KR": {
"select_all": "전체 선택",
"clear_all": "선택 해제",
"quick_select": "유형",
"quick_select": "유형별 필터",
"all_types": "전체",
"search_label": "검색",
"search_placeholder": "제목, 설명, 파일 경로 검색...",
"no_results": "현재 필터와 일치하는 플러그인이 없습니다.",
"selected_count": "{count}개 선택됨",
"install_selected": "선택한 플러그인 설치",
"cancel": "취소",
@@ -363,7 +383,11 @@ SELECTION_DIALOG_TEXTS = {
"ja-JP": {
"select_all": "すべて選択",
"clear_all": "クリア",
"quick_select": "タイプ",
"quick_select": "タイプで絞り込み",
"all_types": "すべて",
"search_label": "検索",
"search_placeholder": "タイトル、説明、ファイルパスを検索...",
"no_results": "現在の条件に一致するプラグインはありません。",
"selected_count": "{count}件を選択",
"install_selected": "選択したプラグインをインストール",
"cancel": "キャンセル",
@@ -375,7 +399,11 @@ SELECTION_DIALOG_TEXTS = {
"fr-FR": {
"select_all": "Tout sélectionner",
"clear_all": "Tout effacer",
"quick_select": "Types",
"quick_select": "Filtrer par type",
"all_types": "Tous",
"search_label": "Rechercher",
"search_placeholder": "Rechercher par titre, description, fichier...",
"no_results": "Aucun plugin ne correspond au filtre actuel.",
"selected_count": "{count} sélectionnés",
"install_selected": "Installer la sélection",
"cancel": "Annuler",
@@ -387,7 +415,11 @@ SELECTION_DIALOG_TEXTS = {
"de-DE": {
"select_all": "Alle auswählen",
"clear_all": "Auswahl löschen",
"quick_select": "Typen",
"quick_select": "Nach Typ filtern",
"all_types": "Alle",
"search_label": "Suchen",
"search_placeholder": "Titel, Beschreibung, Dateipfad durchsuchen...",
"no_results": "Keine Plugins entsprechen dem aktuellen Filter.",
"selected_count": "{count} ausgewählt",
"install_selected": "Auswahl installieren",
"cancel": "Abbrechen",
@@ -399,7 +431,11 @@ SELECTION_DIALOG_TEXTS = {
"es-ES": {
"select_all": "Seleccionar todo",
"clear_all": "Limpiar",
"quick_select": "Tipos",
"quick_select": "Filtrar por tipo",
"all_types": "Todos",
"search_label": "Buscar",
"search_placeholder": "Buscar por titulo, descripcion o archivo...",
"no_results": "Ningun plugin coincide con el filtro actual.",
"selected_count": "{count} seleccionados",
"install_selected": "Instalar seleccionados",
"cancel": "Cancelar",
@@ -411,7 +447,11 @@ SELECTION_DIALOG_TEXTS = {
"it-IT": {
"select_all": "Seleziona tutto",
"clear_all": "Cancella",
"quick_select": "Tipi",
"quick_select": "Filtra per tipo",
"all_types": "Tutti",
"search_label": "Cerca",
"search_placeholder": "Cerca per titolo, descrizione o file...",
"no_results": "Nessun plugin corrisponde al filtro attuale.",
"selected_count": "{count} selezionati",
"install_selected": "Installa selezionati",
"cancel": "Annulla",
@@ -423,7 +463,11 @@ SELECTION_DIALOG_TEXTS = {
"vi-VN": {
"select_all": "Chọn tất cả",
"clear_all": "Bỏ chọn",
"quick_select": "Loại",
"quick_select": "Lọc theo loại",
"all_types": "Tất cả",
"search_label": "Tìm kiếm",
"search_placeholder": "Tìm theo tiêu đề, mô tả, đường dẫn tệp...",
"no_results": "Không có plugin nào khớp với bộ lọc hiện tại.",
"selected_count": "Đã chọn {count}",
"install_selected": "Cài đặt mục đã chọn",
"cancel": "Hủy",
@@ -905,6 +949,8 @@ def _build_selection_dialog_js(
" return;",
" }",
" const selected = new Set(options.map((item) => item.id));",
" let activeFilter = '';",
" let searchTerm = '';",
" const escapeHtml = (value) => String(value ?? '').replace(/[&<>\"']/g, (char) => ({",
" '&': '&amp;',",
" '<': '&lt;',",
@@ -949,9 +995,15 @@ def _build_selection_dialog_js(
" </div>",
" <div id=\"batch-install-plugin-selector-count\" style=\"font-size:13px;font-weight:600;color:#475569\"></div>",
" </div>",
" <div style=\"display:flex;gap:10px;align-items:center;flex-wrap:wrap\">",
" <div style=\"font-size:12px;font-weight:700;color:#475569;text-transform:uppercase;letter-spacing:0.04em\">${escapeHtml(ui.quick_select)}</div>",
" <div id=\"batch-install-plugin-selector-types\" style=\"display:flex;gap:8px;flex-wrap:wrap\"></div>",
" <div style=\"display:grid;gap:10px\">",
" <div style=\"display:flex;gap:10px;align-items:center;flex-wrap:wrap\">",
" <div style=\"font-size:12px;font-weight:700;color:#475569;text-transform:uppercase;letter-spacing:0.04em\">${escapeHtml(ui.quick_select)}</div>",
" <div id=\"batch-install-plugin-selector-types\" style=\"display:flex;gap:8px;flex-wrap:wrap\"></div>",
" </div>",
" <div style=\"display:grid;gap:6px\">",
" <div style=\"font-size:12px;font-weight:700;color:#475569;text-transform:uppercase;letter-spacing:0.04em\">${escapeHtml(ui.search_label)}</div>",
" <input id=\"batch-install-plugin-selector-search\" type=\"text\" placeholder=\"${escapeHtml(ui.search_placeholder)}\" style=\"width:100%;padding:10px 12px;border:1px solid #cbd5e1;border-radius:12px;background:#fff;color:#0f172a;font-size:14px;outline:none;box-sizing:border-box\" />",
" </div>",
" </div>",
" </div>",
" <div id=\"batch-install-plugin-selector-list\" style=\"padding:16px 24px 0;overflow:auto;display:grid;gap:12px;flex:1;min-height:0\"></div>",
@@ -968,6 +1020,7 @@ def _build_selection_dialog_js(
" const countEl = overlay.querySelector('#batch-install-plugin-selector-count');",
" const hintEl = overlay.querySelector('#batch-install-plugin-selector-hint');",
" const typesEl = overlay.querySelector('#batch-install-plugin-selector-types');",
" const searchInput = overlay.querySelector('#batch-install-plugin-selector-search');",
" const submitBtn = overlay.querySelector('#batch-install-plugin-selector-submit');",
" const cancelBtn = overlay.querySelector('#batch-install-plugin-selector-cancel');",
" const selectAllBtn = overlay.querySelector('#batch-install-plugin-selector-select-all');",
@@ -980,35 +1033,34 @@ def _build_selection_dialog_js(
" return groups;",
" }, {});",
" const typeEntries = Object.entries(typeMap);",
" const getVisibleOptions = () => options.filter((item) => {",
" const matchesType = !activeFilter || item.type === activeFilter;",
" const haystack = [item.title, item.description, item.file_path, item.type].join(' ').toLowerCase();",
" const matchesSearch = !searchTerm || haystack.includes(searchTerm);",
" return matchesType && matchesSearch;",
" });",
" const formatMultilineText = (value) => escapeHtml(value).replace(/\\n+/g, '<br />');",
" hintEl.textContent = ui.hint || '';",
" hintEl.style.display = ui.hint ? 'block' : 'none';",
" const renderTypeButtons = () => {",
" typesEl.innerHTML = typeEntries.map(([type, items]) => {",
" const allSelected = items.every((item) => selected.has(item.id));",
" const anySelected = items.some((item) => selected.has(item.id));",
" const background = allSelected ? '#0f172a' : '#ffffff';",
" const color = allSelected ? '#ffffff' : '#0f172a';",
" const border = anySelected ? '#94a3b8' : '#cbd5e1';",
" const filterEntries = [['', options], ...typeEntries];",
" typesEl.innerHTML = filterEntries.map(([type, items]) => {",
" const isActive = activeFilter === type;",
" const background = isActive ? '#0f172a' : '#ffffff';",
" const color = isActive ? '#ffffff' : '#0f172a';",
" const border = isActive ? '#0f172a' : '#cbd5e1';",
" const label = type || ui.all_types;",
" return `",
" <button type=\"button\" data-plugin-type=\"${escapeHtml(type)}\" style=\"padding:7px 12px;border:1px solid ${border};border-radius:999px;background:${background};color:${color};font-size:12px;font-weight:700;cursor:pointer;display:inline-flex;gap:8px;align-items:center\">",
" <span>${escapeHtml(type)}</span>",
" <span style=\"display:inline-flex;align-items:center;justify-content:center;min-width:20px;height:20px;padding:0 6px;border-radius:999px;background:${allSelected ? 'rgba(255,255,255,0.16)' : '#e2e8f0'};color:${allSelected ? '#ffffff' : '#334155'}\">${items.length}</span>",
" <span>${escapeHtml(label)}</span>",
" <span style=\"display:inline-flex;align-items:center;justify-content:center;min-width:20px;height:20px;padding:0 6px;border-radius:999px;background:${isActive ? 'rgba(255,255,255,0.16)' : '#e2e8f0'};color:${isActive ? '#ffffff' : '#334155'}\">${items.length}</span>",
" </button>",
" `;",
" }).join('');",
" typesEl.querySelectorAll('button[data-plugin-type]').forEach((button) => {",
" button.addEventListener('click', () => {",
" const pluginType = button.getAttribute('data-plugin-type') || '';",
" const items = typeMap[pluginType] || [];",
" const allSelected = items.length > 0 && items.every((item) => selected.has(item.id));",
" items.forEach((item) => {",
" if (allSelected) {",
" selected.delete(item.id);",
" } else {",
" selected.add(item.id);",
" }",
" });",
" activeFilter = activeFilter === pluginType ? '' : pluginType;",
" renderList();",
" });",
" });",
@@ -1021,7 +1073,13 @@ def _build_selection_dialog_js(
" renderTypeButtons();",
" };",
" const renderList = () => {",
" listEl.innerHTML = options.map((item) => {",
" const visibleOptions = getVisibleOptions();",
" if (!visibleOptions.length) {",
" listEl.innerHTML = `<div style=\"padding:24px;border:1px dashed #cbd5e1;border-radius:14px;background:#f8fafc;color:#475569;font-size:14px;text-align:center\">${escapeHtml(ui.no_results)}</div>`;",
" updateState();",
" return;",
" }",
" listEl.innerHTML = visibleOptions.map((item) => {",
" const checked = selected.has(item.id) ? 'checked' : '';",
" const description = item.description ? `",
" <div style=\"display:grid;gap:4px\">",
@@ -1078,11 +1136,15 @@ def _build_selection_dialog_js(
" }",
" });",
" selectAllBtn.addEventListener('click', () => {",
" options.forEach((item) => selected.add(item.id));",
" getVisibleOptions().forEach((item) => selected.add(item.id));",
" renderList();",
" });",
" clearAllBtn.addEventListener('click', () => {",
" selected.clear();",
" getVisibleOptions().forEach((item) => selected.delete(item.id));",
" renderList();",
" });",
" searchInput.addEventListener('input', () => {",
" searchTerm = searchInput.value.trim().toLowerCase();",
" renderList();",
" });",
" cancelBtn.addEventListener('click', () => finish(false));",
@@ -1138,6 +1200,10 @@ async def _request_plugin_selection(
"select_all": _selection_t(lang, "select_all"),
"clear_all": _selection_t(lang, "clear_all"),
"quick_select": _selection_t(lang, "quick_select"),
"all_types": _selection_t(lang, "all_types"),
"search_label": _selection_t(lang, "search_label"),
"search_placeholder": _selection_t(lang, "search_placeholder"),
"no_results": _selection_t(lang, "no_results"),
"selected_count": _selection_t(lang, "selected_count", count="{count}"),
"install_selected": _selection_t(lang, "install_selected"),
"cancel": _selection_t(lang, "cancel"),

View File

@@ -8,7 +8,7 @@ Batch Install Plugins from GitHub v1.1.0 upgrades the install confirmation step
## Highlights
- **Interactive Selection Dialog**: Opens a checkbox-based browser dialog with type tags and visible plugin descriptions
- **Interactive Selection Dialog**: Opens a checkbox-based browser dialog with type filters, keyword search, and visible plugin descriptions
- **Selective Installation**: The install loop now runs only for the plugins the user keeps selected
- **Repository Context**: Displays the current repository and only shows useful exclusion information inside the dialog
- **Localized UI**: Dialog controls are localized for all supported languages

View File

@@ -8,7 +8,7 @@ Batch Install Plugins from GitHub v1.1.0 将原本的安装确认步骤升级为
## 亮点
- **交互式选择对话框**:不再只使用基础 confirmation 事件,而是打开带类型标签、描述信息和复选框的浏览器对话框
- **交互式选择对话框**:不再只使用基础 confirmation 事件,而是打开带类型筛选、关键词搜索、描述信息和复选框的浏览器对话框
- **选择性安装**:安装循环只会处理用户最终保留勾选的插件
- **仓库上下文**:对话框中会显示当前仓库,并且只展示真正有用的排除信息
- **本地化 UI**:对话框控件已为所有支持语言提供本地化文本