feat: add interactive selection dialog to batch installer

This commit is contained in:
fujie
2026-03-16 14:17:51 +08:00
parent e992b8a3bd
commit 00afae4a6d
13 changed files with 514 additions and 106 deletions

View File

@@ -1,6 +1,6 @@
# Batch Install Plugins from GitHub # Batch Install Plugins from GitHub
**Author:** [Fu-Jie](https://github.com/Fu-Jie) | **Version:** 1.0.0 | **Project:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) **Author:** [Fu-Jie](https://github.com/Fu-Jie) | **Version:** 1.1.0 | **Project:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions)
One-click batch install plugins from GitHub repositories to your OpenWebUI instance. One-click batch install plugins from GitHub repositories to your OpenWebUI instance.
@@ -10,7 +10,7 @@ One-click batch install plugins from GitHub repositories to your OpenWebUI insta
- **Auto-Update**: Automatically updates previously installed plugins - **Auto-Update**: Automatically updates previously installed plugins
- **Public GitHub Support**: Install plugins from any public GitHub repository - **Public GitHub Support**: Install plugins from any public GitHub repository
- **Multi-Type Support**: Supports Pipe, Action, Filter, and Tool plugins - **Multi-Type Support**: Supports Pipe, Action, Filter, and Tool plugins
- **Confirmation**: Shows plugin list before installing, allows selective installation - **Interactive Selection Dialog**: Review the filtered list, check the plugins you want, then install only that subset
- **i18n**: Supports 11 languages - **i18n**: Supports 11 languages
## Flow ## Flow
@@ -32,8 +32,8 @@ User Input
┌─────────────────────────────────────┐ ┌─────────────────────────────────────┐
│ Show Confirmation Dialog │ │ Show Selection Dialog
│ (list plugins + exclude hint) │ (checkbox list + exclude hint) │
└─────────────────────────────────────┘ └─────────────────────────────────────┘
├── [Cancel] → End ├── [Cancel] → End
@@ -59,6 +59,8 @@ User Input
Each request handles one repository. To mix repositories, send another request after the previous installation completes. 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 check exactly which plugins to install before the API calls start.
## Quick Start: Install Popular Collections ## Quick Start: Install Popular Collections
Copy any of these prompts and paste them into your chat: Copy any of these prompts and paste them into your chat:
@@ -127,11 +129,11 @@ For other repositories:
| `SKIP_KEYWORDS` | `test,verify,example,template,mock` | Comma-separated keywords to skip | | `SKIP_KEYWORDS` | `test,verify,example,template,mock` | Comma-separated keywords to skip |
| `TIMEOUT` | `20` | Request timeout in seconds | | `TIMEOUT` | `20` | Request timeout in seconds |
## Confirmation Timeout ## Selection Dialog Timeout
User confirmation dialogs have a default timeout of **2 minutes (120 seconds)**, allowing sufficient time for users to: The plugin selection dialog has a default timeout of **2 minutes (120 seconds)**, allowing sufficient time for users to:
- Read and review the plugin list - Read and review the plugin list
- Make installation decisions - Check or uncheck the plugins they want
- Handle network delays - Handle network delays
## Support ## Support

View File

@@ -1,6 +1,6 @@
# Batch Install Plugins from GitHub # Batch Install Plugins from GitHub
**作者:** [Fu-Jie](https://github.com/Fu-Jie) | **版本:** 1.0.0 | **项目:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) **作者:** [Fu-Jie](https://github.com/Fu-Jie) | **版本:** 1.1.0 | **项目:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions)
一键将 GitHub 仓库中的插件批量安装到你的 OpenWebUI 实例。 一键将 GitHub 仓库中的插件批量安装到你的 OpenWebUI 实例。
@@ -10,7 +10,7 @@
- 自动更新:自动更新之前安装过的插件 - 自动更新:自动更新之前安装过的插件
- 公开 GitHub 支持:支持从任何公开 GitHub 仓库安装插件 - 公开 GitHub 支持:支持从任何公开 GitHub 仓库安装插件
- 多类型支持:支持 Pipe、Action、Filter 和 Tool 插件 - 多类型支持:支持 Pipe、Action、Filter 和 Tool 插件
- 安装确认:安装前显示插件列表,支持选择性安装 - 交互式选择对话框:先查看过滤后的列表,再勾选要安装的插件,只安装所选子集
- 国际化:支持 11 种语言 - 国际化:支持 11 种语言
## 流程 ## 流程
@@ -32,8 +32,8 @@
┌─────────────────────────────────────┐ ┌─────────────────────────────────────┐
│ 显示确认对话框 │ │ 显示选择对话框 │
│ (插件列表 + 排除提示) │ │ (复选列表 + 排除提示) │
└─────────────────────────────────────┘ └─────────────────────────────────────┘
├── [取消] → 结束 ├── [取消] → 结束
@@ -59,6 +59,8 @@
每次请求处理一个仓库。如需混合多个来源,请在上一次安装完成后再发起下一次请求。 每次请求处理一个仓库。如需混合多个来源,请在上一次安装完成后再发起下一次请求。
在插件发现和过滤完成后OpenWebUI 会通过 `execute` 事件打开浏览器选择对话框,你可以先勾选真正想安装的插件,再开始调用安装 API。
## 快速开始:安装热门插件集 ## 快速开始:安装热门插件集
复制以下任一提示词,粘贴到你的对话框中: 复制以下任一提示词,粘贴到你的对话框中:
@@ -127,11 +129,11 @@
| `SKIP_KEYWORDS` | `test,verify,example,template,mock` | 逗号分隔的跳过关键词 | | `SKIP_KEYWORDS` | `test,verify,example,template,mock` | 逗号分隔的跳过关键词 |
| `TIMEOUT` | `20` | 请求超时时间(秒)| | `TIMEOUT` | `20` | 请求超时时间(秒)|
## 确认超时时间 ## 选择对话框超时时间
用户确认对话框的默认超时时间为 **2 分钟120 秒)**,为用户提供充足的时间来: 插件选择对话框的默认超时时间为 **2 分钟120 秒)**,为用户提供充足的时间来:
- 阅读和查看插件列表 - 阅读和查看插件列表
- 做出安装决定 - 勾选或取消勾选想安装的插件
- 处理网络延迟 - 处理网络延迟
## 支持 ## 支持

View File

@@ -1,6 +1,6 @@
# Batch Install Plugins from GitHub # Batch Install Plugins from GitHub
| By [Fu-Jie](https://github.com/Fu-Jie) · v1.0.0 | [⭐ Star this repo](https://github.com/Fu-Jie/openwebui-extensions) | | By [Fu-Jie](https://github.com/Fu-Jie) · v1.1.0 | [⭐ Star this repo](https://github.com/Fu-Jie/openwebui-extensions) |
| :--- | ---: | | :--- | ---: |
| ![followers](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_followers.json&label=%F0%9F%91%A5&style=flat) | ![points](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_points.json&label=%E2%AD%90&style=flat) | ![top](https://img.shields.io/badge/%F0%9F%8F%86-Top%20%3C1%25-10b981?style=flat) | ![contributions](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_contributions.json&label=%F0%9F%93%A6&style=flat) | ![downloads](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_downloads.json&label=%E2%AC%87%EF%B8%8F&style=flat) | ![saves](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_saves.json&label=%F0%9F%92%BE&style=flat) | ![views](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_views.json&label=%F0%9F%91%81%EF%B8%8F&style=flat) | | ![followers](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_followers.json&label=%F0%9F%91%A5&style=flat) | ![points](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_points.json&label=%E2%AD%90&style=flat) | ![top](https://img.shields.io/badge/%F0%9F%8F%86-Top%20%3C1%25-10b981?style=flat) | ![contributions](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_contributions.json&label=%F0%9F%93%A6&style=flat) | ![downloads](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_downloads.json&label=%E2%AC%87%EF%B8%8F&style=flat) | ![saves](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_saves.json&label=%F0%9F%92%BE&style=flat) | ![views](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_views.json&label=%F0%9F%91%81%EF%B8%8F&style=flat) |
@@ -14,7 +14,7 @@ One-click batch install plugins from GitHub repositories to your OpenWebUI insta
- **Auto-Update**: Automatically updates previously installed plugins - **Auto-Update**: Automatically updates previously installed plugins
- **Public GitHub Support**: Install plugins from any public GitHub repository - **Public GitHub Support**: Install plugins from any public GitHub repository
- **Multi-Type Support**: Supports Pipe, Action, Filter, and Tool plugins - **Multi-Type Support**: Supports Pipe, Action, Filter, and Tool plugins
- **Confirmation**: Shows plugin list before installing, allows selective installation - **Interactive Selection Dialog**: Review the filtered list, check the plugins you want, then install only that subset
- **i18n**: Supports 11 languages - **i18n**: Supports 11 languages
## Flow ## Flow
@@ -36,8 +36,8 @@ User Input
┌─────────────────────────────────────┐ ┌─────────────────────────────────────┐
│ Show Confirmation Dialog │ │ Show Selection Dialog
│ (list plugins + exclude hint) │ (checkbox list + exclude hint) │
└─────────────────────────────────────┘ └─────────────────────────────────────┘
├── [Cancel] → End ├── [Cancel] → End
@@ -63,6 +63,8 @@ User Input
Each request handles one repository. To mix repositories, send another request after the previous installation completes. 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 check exactly which plugins to install before the API calls start.
## Quick Start: Install Popular Collections ## Quick Start: Install Popular Collections
Copy any of these prompts and paste them into your chat: Copy any of these prompts and paste them into your chat:
@@ -131,11 +133,11 @@ For other repositories:
| `SKIP_KEYWORDS` | `test,verify,example,template,mock` | Comma-separated keywords to skip | | `SKIP_KEYWORDS` | `test,verify,example,template,mock` | Comma-separated keywords to skip |
| `TIMEOUT` | `20` | Request timeout in seconds | | `TIMEOUT` | `20` | Request timeout in seconds |
## Confirmation Timeout ## Selection Dialog Timeout
User confirmation dialogs have a default timeout of **2 minutes (120 seconds)**, allowing sufficient time for users to: The plugin selection dialog has a default timeout of **2 minutes (120 seconds)**, allowing sufficient time for users to:
- Read and review the plugin list - Read and review the plugin list
- Make installation decisions - Check or uncheck the plugins they want
- Handle network delays - Handle network delays
## Support ## Support

View File

@@ -1,6 +1,6 @@
# Batch Install Plugins from GitHub # Batch Install Plugins from GitHub
| 作者:[Fu-Jie](https://github.com/Fu-Jie) · v1.0.0 | [⭐ 点个 Star 支持项目](https://github.com/Fu-Jie/openwebui-extensions) | | 作者:[Fu-Jie](https://github.com/Fu-Jie) · v1.1.0 | [⭐ 点个 Star 支持项目](https://github.com/Fu-Jie/openwebui-extensions) |
| :--- | ---: | | :--- | ---: |
| ![followers](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_followers.json&label=%F0%9F%91%A5&style=flat) | ![points](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_points.json&label=%E2%AD%90&style=flat) | ![top](https://img.shields.io/badge/%F0%9F%8F%86-Top%20%3C1%25-10b981?style=flat) | ![contributions](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_contributions.json&label=%F0%9F%93%A6&style=flat) | ![downloads](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_downloads.json&label=%E2%AC%87%EF%B8%8F&style=flat) | ![saves](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_saves.json&label=%F0%9F%92%BE&style=flat) | ![views](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_views.json&label=%F0%9F%91%81%EF%B8%8F&style=flat) | | ![followers](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_followers.json&label=%F0%9F%91%A5&style=flat) | ![points](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_points.json&label=%E2%AD%90&style=flat) | ![top](https://img.shields.io/badge/%F0%9F%8F%86-Top%20%3C1%25-10b981?style=flat) | ![contributions](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_contributions.json&label=%F0%9F%93%A6&style=flat) | ![downloads](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_downloads.json&label=%E2%AC%87%EF%B8%8F&style=flat) | ![saves](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_saves.json&label=%F0%9F%92%BE&style=flat) | ![views](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_views.json&label=%F0%9F%91%81%EF%B8%8F&style=flat) |
@@ -14,7 +14,7 @@
- 自动更新:自动更新之前安装过的插件 - 自动更新:自动更新之前安装过的插件
- 公开 GitHub 支持:支持从任何公开 GitHub 仓库安装插件 - 公开 GitHub 支持:支持从任何公开 GitHub 仓库安装插件
- 多类型支持:支持 Pipe、Action、Filter 和 Tool 插件 - 多类型支持:支持 Pipe、Action、Filter 和 Tool 插件
- 安装确认:安装前显示插件列表,支持选择性安装 - 交互式选择对话框:先查看过滤后的列表,再勾选要安装的插件,只安装所选子集
- 国际化:支持 11 种语言 - 国际化:支持 11 种语言
## 流程 ## 流程
@@ -36,8 +36,8 @@
┌─────────────────────────────────────┐ ┌─────────────────────────────────────┐
│ 显示确认对话框 │ │ 显示选择对话框 │
│ (插件列表 + 排除提示) │ │ (复选列表 + 排除提示) │
└─────────────────────────────────────┘ └─────────────────────────────────────┘
├── [取消] → 结束 ├── [取消] → 结束
@@ -63,6 +63,8 @@
每次请求处理一个仓库。如需混合多个来源,请在上一次安装完成后再发起下一次请求。 每次请求处理一个仓库。如需混合多个来源,请在上一次安装完成后再发起下一次请求。
在插件发现和过滤完成后OpenWebUI 会通过 `execute` 事件打开浏览器选择对话框,你可以先勾选真正想安装的插件,再开始调用安装 API。
## 快速开始:安装热门插件集 ## 快速开始:安装热门插件集
复制以下任一提示词,粘贴到你的对话框中: 复制以下任一提示词,粘贴到你的对话框中:
@@ -131,11 +133,11 @@
| `SKIP_KEYWORDS` | `test,verify,example,template,mock` | 逗号分隔的跳过关键词 | | `SKIP_KEYWORDS` | `test,verify,example,template,mock` | 逗号分隔的跳过关键词 |
| `TIMEOUT` | `20` | 请求超时时间(秒)| | `TIMEOUT` | `20` | 请求超时时间(秒)|
## 确认超时时间 ## 选择对话框超时时间
用户确认对话框的默认超时时间为 **2 分钟120 秒)**,为用户提供充足的时间来: 插件选择对话框的默认超时时间为 **2 分钟120 秒)**,为用户提供充足的时间来:
- 阅读和查看插件列表 - 阅读和查看插件列表
- 做出安装决定 - 勾选或取消勾选想安装的插件
- 处理网络延迟 - 处理网络延迟
## 支持 ## 支持

View File

@@ -4,6 +4,6 @@ OpenWebUI native Tool plugins that can be used across models.
## Available Tool Plugins ## Available Tool Plugins
- [Batch Install Plugins from GitHub](batch-install-plugins-tool.md) (v1.0.0) - One-click batch install plugins from GitHub repositories with confirmation and multi-language support. - [Batch Install Plugins from GitHub](batch-install-plugins-tool.md) (v1.1.0) - One-click batch install plugins from GitHub repositories with an interactive selection dialog and multi-language support.
- [OpenWebUI Skills Manager Tool](openwebui-skills-manager-tool.md) (v0.3.0) - Simple native skill management (`list/show/install/create/update/delete`). - [OpenWebUI Skills Manager Tool](openwebui-skills-manager-tool.md) (v0.3.0) - Simple native skill management (`list/show/install/create/update/delete`).
- [Smart Mind Map Tool](smart-mind-map-tool.md) (v1.0.0) - Intelligently analyzes text content and proactively generates interactive mind maps to help users structure and visualize knowledge. - [Smart Mind Map Tool](smart-mind-map-tool.md) (v1.0.0) - Intelligently analyzes text content and proactively generates interactive mind maps to help users structure and visualize knowledge.

View File

@@ -4,6 +4,6 @@
## 可用 Tool 插件 ## 可用 Tool 插件
- [Batch Install Plugins from GitHub](batch-install-plugins-tool.zh.md) (v1.0.0) - 一键从 GitHub 仓库批量安装插件,支持确认和多语言。 - [Batch Install Plugins from GitHub](batch-install-plugins-tool.zh.md) (v1.1.0) - 一键从 GitHub 仓库批量安装插件,支持交互式选择对话框和多语言。
- [OpenWebUI Skills 管理工具](openwebui-skills-manager-tool.zh.md) (v0.3.0) - 简化技能管理(`list/show/install/create/update/delete`)。 - [OpenWebUI Skills 管理工具](openwebui-skills-manager-tool.zh.md) (v0.3.0) - 简化技能管理(`list/show/install/create/update/delete`)。
- [智能思维导图工具 (Smart Mind Map Tool)](smart-mind-map-tool.zh.md) (v1.0.0) - 智能分析文本内容并主动生成交互式思维导图,帮助用户结构化与可视化知识。 - [智能思维导图工具 (Smart Mind Map Tool)](smart-mind-map-tool.zh.md) (v1.0.0) - 智能分析文本内容并主动生成交互式思维导图,帮助用户结构化与可视化知识。

View File

@@ -1,21 +1,21 @@
# 🎉 Introducing Batch Install Plugins v1.0.0 # 🎉 Batch Install Plugins v1.1.0
## Headline ## Headline
**One-Click Batch Installation of OpenWebUI Plugins - Solving the Plugin Setup Headache** **Interactive Plugin Picker for OpenWebUI Batch Installation**
## Introduction ## Introduction
Installing plugins in OpenWebUI used to be tedious: searching for plugins, downloading them one by one, and hoping everything works in your environment. Today, we're excited to announce **Batch Install Plugins from GitHub** v1.0.0 — a powerful new tool that transforms plugin installation from a chore into a single command. Installing plugins in OpenWebUI should not feel like an all-or-nothing jump. With **Batch Install Plugins from GitHub** v1.1.0, the workflow now opens an interactive browser dialog so users can review the filtered list and choose exactly which plugins to install before the API requests begin.
## Key Highlights ## Key Highlights
### 🚀 One-Click Bulk Installation ### 🚀 Interactive Plugin Selection
- Install multiple plugins from any public GitHub repository with a single command - Uses the OpenWebUI `execute` event to open a custom browser dialog
- Automatically discovers plugins and validates them - Displays the filtered plugin list with checkboxes, repository context, and exclude hints
- Updates previously installed plugins seamlessly - Installs only the plugins the user keeps selected
### ✅ Smart Safety Features ### ✅ Smart Safety Features
- Shows a confirmation dialog with the plugin list before installation - Replaces the basic confirmation event with a richer selective install flow
- Users can review and approve before proceeding - Users can uncheck plugins they do not want without rewriting the request
- Automatically excludes the tool itself from installation - Automatically excludes the tool itself from installation
### 🌍 Multi-Repository Support ### 🌍 Multi-Repository Support
@@ -43,7 +43,7 @@ Each request handles one repository. To combine multiple repositories, send anot
``` ```
"Install all plugins from Fu-Jie/openwebui-extensions" "Install all plugins from Fu-Jie/openwebui-extensions"
``` ```
Review the confirmation dialog, approve, and the plugins are installed. Review the selection dialog, keep the plugins you want checked, and then install them.
2. **Add a Community Collection** 2. **Add a Community Collection**
``` ```
@@ -110,8 +110,8 @@ Each line below is a separate request:
- **Async Architecture**: Non-blocking I/O for better performance - **Async Architecture**: Non-blocking I/O for better performance
- **httpx Integration**: Modern async HTTP client with timeout protection - **httpx Integration**: Modern async HTTP client with timeout protection
- **Comprehensive Tests**: 8 regression tests with 100% pass rate - **Selective Install Flow**: The install loop now runs only for the checked plugin subset
- **Full Event Support**: Proper OpenWebUI event injection with fallback handling - **Full Event Support**: Proper OpenWebUI `execute` event handling with fallback behavior
## Installation ## Installation
@@ -123,7 +123,7 @@ Each line below is a separate request:
## Links ## Links
- **GitHub Repository**: https://github.com/Fu-Jie/openwebui-extensions/tree/main/plugins/tools/batch-install-plugins - **GitHub Repository**: https://github.com/Fu-Jie/openwebui-extensions/tree/main/plugins/tools/batch-install-plugins
- **Release Notes**: https://github.com/Fu-Jie/openwebui-extensions/blob/main/plugins/tools/batch-install-plugins/v1.0.0.md - **Release Notes**: https://github.com/Fu-Jie/openwebui-extensions/blob/main/plugins/tools/batch-install-plugins/v1.1.0.md
## Community Love ## Community Love

View File

@@ -1,21 +1,21 @@
# 🎉 Batch Install Plugins 首发 v1.0.0 # 🎉 Batch Install Plugins v1.1.0
## 标题 ## 标题
**一键批量安装 OpenWebUI 插件 - 解决装机烦恼** ** OpenWebUI 批量安装带来可勾选的交互式插件选择器**
## 前言 ## 前言
在 OpenWebUI 中安装插件曾经很麻烦:逐个搜索、逐个下载、祈祷一切顺利。今天,我们欣然宣布 **Batch Install Plugins from GitHub** v1.0.0 的问世 — 一款强大的新工具,让插件安装从苦差事变成一条简单命令 批量安装不应该是“全装或不装”的二选一。现在,**Batch Install Plugins from GitHub** v1.1.0 新增了基于浏览器的交互式选择对话框,用户可以先查看过滤后的插件列表,再勾选真正要安装的插件,然后才开始调用安装 API
## 核心特性 ## 核心特性
### 🚀 一键批量安装 ### 🚀 交互式插件选择
- 从任意公开 GitHub 仓库用一条命令安装多个插件 - 基于 OpenWebUI 的 `execute` 事件打开自定义浏览器选择对话框
- 自动发现插件并进行验证 - 显示过滤后的插件列表、仓库信息与排除提示
- 无缝更新已安装的插件 - 只安装用户保留勾选的插件
### ✅ 智能安全保障 ### ✅ 智能安全保障
- 安装前显示插件列表确认对话框 - 用更丰富的选择流程替代基础 confirmation 事件
- 用户可在安装前查看和审批 - 用户无需改写请求,也能取消勾选不想安装的插件
- 自动排除工具自身,避免重复安装 - 自动排除工具自身,避免重复安装
### 🌍 多仓库支持 ### 🌍 多仓库支持
@@ -43,7 +43,7 @@
``` ```
"安装 Fu-Jie/openwebui-extensions 中的所有插件" "安装 Fu-Jie/openwebui-extensions 中的所有插件"
``` ```
查看确认对话框,批准后开始安装。 查看选择对话框,保留想安装的勾选项后开始安装。
2. **再添加社区合集** 2. **再添加社区合集**
``` ```
@@ -110,8 +110,8 @@ OpenRouter API pipe 集成,提供高级模型访问。
- **异步架构**:非阻塞 I/O性能更优 - **异步架构**:非阻塞 I/O性能更优
- **httpx 集成**:现代化异步 HTTP 客户端,包含超时保护 - **httpx 集成**:现代化异步 HTTP 客户端,包含超时保护
- **完整测试**8 个回归测试100% 通过率 - **选择性安装流程**:安装循环只会处理用户最终勾选的插件子集
- **完整事件支持**:正确处理 OpenWebUI 事件注入,提供回退机制 - **完整事件支持**:正确处理 OpenWebUI `execute` 事件,并保留回退行为
## 安装方法 ## 安装方法
@@ -123,7 +123,7 @@ OpenRouter API pipe 集成,提供高级模型访问。
## 相关链接 ## 相关链接
- **GitHub 仓库**https://github.com/Fu-Jie/openwebui-extensions/tree/main/plugins/tools/batch-install-plugins - **GitHub 仓库**https://github.com/Fu-Jie/openwebui-extensions/tree/main/plugins/tools/batch-install-plugins
- **发布说明**https://github.com/Fu-Jie/openwebui-extensions/blob/main/plugins/tools/batch-install-plugins/v1.0.0_CN.md - **发布说明**https://github.com/Fu-Jie/openwebui-extensions/blob/main/plugins/tools/batch-install-plugins/v1.1.0_CN.md
## 社区支持 ## 社区支持

View File

@@ -1,6 +1,6 @@
# Batch Install Plugins from GitHub # Batch Install Plugins from GitHub
| By [Fu-Jie](https://github.com/Fu-Jie) · v1.0.0 | [⭐ Star this repo](https://github.com/Fu-Jie/openwebui-extensions) | | By [Fu-Jie](https://github.com/Fu-Jie) · v1.1.0 | [⭐ Star this repo](https://github.com/Fu-Jie/openwebui-extensions) |
| :--- | ---: | | :--- | ---: |
| ![followers](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_followers.json&label=%F0%9F%91%A5&style=flat) | ![points](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_points.json&label=%E2%AD%90&style=flat) | ![top](https://img.shields.io/badge/%F0%9F%8F%86-Top%20%3C1%25-10b981?style=flat) | ![contributions](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_contributions.json&label=%F0%9F%93%A6&style=flat) | ![downloads](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_downloads.json&label=%E2%AC%87%EF%B8%8F&style=flat) | ![saves](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_saves.json&label=%F0%9F%92%BE&style=flat) | ![views](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_views.json&label=%F0%9F%91%81%EF%B8%8F&style=flat) | | ![followers](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_followers.json&label=%F0%9F%91%A5&style=flat) | ![points](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_points.json&label=%E2%AD%90&style=flat) | ![top](https://img.shields.io/badge/%F0%9F%8F%86-Top%20%3C1%25-10b981?style=flat) | ![contributions](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_contributions.json&label=%F0%9F%93%A6&style=flat) | ![downloads](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_downloads.json&label=%E2%AC%87%EF%B8%8F&style=flat) | ![saves](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_saves.json&label=%F0%9F%92%BE&style=flat) | ![views](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_views.json&label=%F0%9F%91%81%EF%B8%8F&style=flat) |
@@ -14,7 +14,7 @@ One-click batch install plugins from GitHub repositories to your OpenWebUI insta
- **Auto-Update**: Automatically updates previously installed plugins - **Auto-Update**: Automatically updates previously installed plugins
- **Public GitHub Support**: Install plugins from any public GitHub repository - **Public GitHub Support**: Install plugins from any public GitHub repository
- **Multi-Type Support**: Supports Pipe, Action, Filter, and Tool plugins - **Multi-Type Support**: Supports Pipe, Action, Filter, and Tool plugins
- **Confirmation**: Shows plugin list before installing, allows selective installation - **Interactive Selection Dialog**: Review the filtered list, check the plugins you want, then install only that subset
- **i18n**: Supports 11 languages - **i18n**: Supports 11 languages
## Flow ## Flow
@@ -36,8 +36,8 @@ User Input
┌─────────────────────────────────────┐ ┌─────────────────────────────────────┐
│ Show Confirmation Dialog │ │ Show Selection Dialog
│ (list plugins + exclude hint) │ (checkbox list + exclude hint) │
└─────────────────────────────────────┘ └─────────────────────────────────────┘
├── [Cancel] → End ├── [Cancel] → End
@@ -63,6 +63,8 @@ User Input
Each request handles one repository. To mix repositories, send another request after the previous installation completes. 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 check exactly which plugins to install before the API calls start.
## Quick Start: Install Popular Collections ## Quick Start: Install Popular Collections
Copy any of these prompts and paste them into your chat: Copy any of these prompts and paste them into your chat:
@@ -131,11 +133,11 @@ For other repositories:
| `SKIP_KEYWORDS` | `test,verify,example,template,mock` | Comma-separated keywords to skip | | `SKIP_KEYWORDS` | `test,verify,example,template,mock` | Comma-separated keywords to skip |
| `TIMEOUT` | `20` | Request timeout in seconds | | `TIMEOUT` | `20` | Request timeout in seconds |
## Confirmation Timeout ## Selection Dialog Timeout
User confirmation dialogs have a default timeout of **2 minutes (120 seconds)**, allowing sufficient time for users to: The plugin selection dialog has a default timeout of **2 minutes (120 seconds)**, allowing sufficient time for users to:
- Read and review the plugin list - Read and review the plugin list
- Make installation decisions - Check or uncheck the plugins they want
- Handle network delays - Handle network delays
## Support ## Support

View File

@@ -1,6 +1,6 @@
# Batch Install Plugins from GitHub # Batch Install Plugins from GitHub
| 作者:[Fu-Jie](https://github.com/Fu-Jie) · v1.0.0 | [⭐ 点个 Star 支持项目](https://github.com/Fu-Jie/openwebui-extensions) | | 作者:[Fu-Jie](https://github.com/Fu-Jie) · v1.1.0 | [⭐ 点个 Star 支持项目](https://github.com/Fu-Jie/openwebui-extensions) |
| :--- | ---: | | :--- | ---: |
| ![followers](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_followers.json&label=%F0%9F%91%A5&style=flat) | ![points](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_points.json&label=%E2%AD%90&style=flat) | ![top](https://img.shields.io/badge/%F0%9F%8F%86-Top%20%3C1%25-10b981?style=flat) | ![contributions](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_contributions.json&label=%F0%9F%93%A6&style=flat) | ![downloads](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_downloads.json&label=%E2%AC%87%EF%B8%8F&style=flat) | ![saves](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_saves.json&label=%F0%9F%92%BE&style=flat) | ![views](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_views.json&label=%F0%9F%91%81%EF%B8%8F&style=flat) | | ![followers](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_followers.json&label=%F0%9F%91%A5&style=flat) | ![points](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_points.json&label=%E2%AD%90&style=flat) | ![top](https://img.shields.io/badge/%F0%9F%8F%86-Top%20%3C1%25-10b981?style=flat) | ![contributions](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_contributions.json&label=%F0%9F%93%A6&style=flat) | ![downloads](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_downloads.json&label=%E2%AC%87%EF%B8%8F&style=flat) | ![saves](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_saves.json&label=%F0%9F%92%BE&style=flat) | ![views](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_views.json&label=%F0%9F%91%81%EF%B8%8F&style=flat) |
@@ -14,7 +14,7 @@
- 自动更新:自动更新之前安装过的插件 - 自动更新:自动更新之前安装过的插件
- 公开 GitHub 支持:支持从任何公开 GitHub 仓库安装插件 - 公开 GitHub 支持:支持从任何公开 GitHub 仓库安装插件
- 多类型支持:支持 Pipe、Action、Filter 和 Tool 插件 - 多类型支持:支持 Pipe、Action、Filter 和 Tool 插件
- 安装确认:安装前显示插件列表,支持选择性安装 - 交互式选择对话框:先查看过滤后的列表,再勾选要安装的插件,只安装所选子集
- 国际化:支持 11 种语言 - 国际化:支持 11 种语言
## 流程 ## 流程
@@ -36,8 +36,8 @@
┌─────────────────────────────────────┐ ┌─────────────────────────────────────┐
│ 显示确认对话框 │ │ 显示选择对话框 │
│ (插件列表 + 排除提示) │ │ (复选列表 + 排除提示) │
└─────────────────────────────────────┘ └─────────────────────────────────────┘
├── [取消] → 结束 ├── [取消] → 结束
@@ -63,6 +63,8 @@
每次请求处理一个仓库。如需混合多个来源,请在上一次安装完成后再发起下一次请求。 每次请求处理一个仓库。如需混合多个来源,请在上一次安装完成后再发起下一次请求。
在插件发现和过滤完成后OpenWebUI 会通过 `execute` 事件打开浏览器选择对话框,你可以先勾选真正想安装的插件,再开始调用安装 API。
## 快速开始:安装热门插件集 ## 快速开始:安装热门插件集
复制以下任一提示词,粘贴到你的对话框中: 复制以下任一提示词,粘贴到你的对话框中:
@@ -131,11 +133,11 @@
| `SKIP_KEYWORDS` | `test,verify,example,template,mock` | 逗号分隔的跳过关键词 | | `SKIP_KEYWORDS` | `test,verify,example,template,mock` | 逗号分隔的跳过关键词 |
| `TIMEOUT` | `20` | 请求超时时间(秒)| | `TIMEOUT` | `20` | 请求超时时间(秒)|
## 确认超时时间 ## 选择对话框超时时间
用户确认对话框的默认超时时间为 **2 分钟120 秒)**,为用户提供充足的时间来: 插件选择对话框的默认超时时间为 **2 分钟120 秒)**,为用户提供充足的时间来:
- 阅读和查看插件列表 - 阅读和查看插件列表
- 做出安装决定 - 勾选或取消勾选想安装的插件
- 处理网络延迟 - 处理网络延迟
## 支持 ## 支持

View File

@@ -3,7 +3,8 @@ title: Batch Install Plugins from GitHub
author: Fu-Jie author: Fu-Jie
author_url: https://github.com/Fu-Jie/openwebui-extensions author_url: https://github.com/Fu-Jie/openwebui-extensions
funding_url: https://github.com/open-webui funding_url: https://github.com/open-webui
version: 1.0.0 version: 1.1.0
openwebui_id: c9fd6e80-d58f-4312-8fbb-214d86bbe599
description: One-click batch install plugins from GitHub repositories to your OpenWebUI instance. description: One-click batch install plugins from GitHub repositories to your OpenWebUI instance.
""" """
@@ -298,6 +299,119 @@ TRANSLATIONS = {
FALLBACK_MAP = {"zh": "zh-CN", "zh-TW": "zh-TW", "zh-HK": "zh-HK", "en": "en-US", "ko": "ko-KR", "ja": "ja-JP", "fr": "fr-FR", "de": "de-DE", "es": "es-ES", "it": "it-IT", "vi": "vi-VN"} FALLBACK_MAP = {"zh": "zh-CN", "zh-TW": "zh-TW", "zh-HK": "zh-HK", "en": "en-US", "ko": "ko-KR", "ja": "ja-JP", "fr": "fr-FR", "de": "de-DE", "es": "es-ES", "it": "it-IT", "vi": "vi-VN"}
SELECTION_DIALOG_TEXTS = {
"en-US": {
"select_all": "Select all",
"clear_all": "Clear all",
"selected_count": "{count} selected",
"install_selected": "Install Selected",
"cancel": "Cancel",
"version_label": "Version",
"file_label": "File",
"repo_label": "Repository",
},
"zh-CN": {
"select_all": "全选",
"clear_all": "清空",
"selected_count": "已选 {count}",
"install_selected": "安装所选插件",
"cancel": "取消",
"version_label": "版本",
"file_label": "文件",
"repo_label": "仓库",
},
"zh-HK": {
"select_all": "全選",
"clear_all": "清空",
"selected_count": "已選 {count}",
"install_selected": "安裝所選外掛",
"cancel": "取消",
"version_label": "版本",
"file_label": "檔案",
"repo_label": "倉庫",
},
"zh-TW": {
"select_all": "全選",
"clear_all": "清空",
"selected_count": "已選 {count}",
"install_selected": "安裝所選外掛",
"cancel": "取消",
"version_label": "版本",
"file_label": "檔案",
"repo_label": "倉庫",
},
"ko-KR": {
"select_all": "전체 선택",
"clear_all": "선택 해제",
"selected_count": "{count}개 선택됨",
"install_selected": "선택한 플러그인 설치",
"cancel": "취소",
"version_label": "버전",
"file_label": "파일",
"repo_label": "저장소",
},
"ja-JP": {
"select_all": "すべて選択",
"clear_all": "クリア",
"selected_count": "{count}件を選択",
"install_selected": "選択したプラグインをインストール",
"cancel": "キャンセル",
"version_label": "バージョン",
"file_label": "ファイル",
"repo_label": "リポジトリ",
},
"fr-FR": {
"select_all": "Tout sélectionner",
"clear_all": "Tout effacer",
"selected_count": "{count} sélectionnés",
"install_selected": "Installer la sélection",
"cancel": "Annuler",
"version_label": "Version",
"file_label": "Fichier",
"repo_label": "Dépôt",
},
"de-DE": {
"select_all": "Alle auswählen",
"clear_all": "Auswahl löschen",
"selected_count": "{count} ausgewählt",
"install_selected": "Auswahl installieren",
"cancel": "Abbrechen",
"version_label": "Version",
"file_label": "Datei",
"repo_label": "Repository",
},
"es-ES": {
"select_all": "Seleccionar todo",
"clear_all": "Limpiar",
"selected_count": "{count} seleccionados",
"install_selected": "Instalar seleccionados",
"cancel": "Cancelar",
"version_label": "Versión",
"file_label": "Archivo",
"repo_label": "Repositorio",
},
"it-IT": {
"select_all": "Seleziona tutto",
"clear_all": "Cancella",
"selected_count": "{count} selezionati",
"install_selected": "Installa selezionati",
"cancel": "Annulla",
"version_label": "Versione",
"file_label": "File",
"repo_label": "Repository",
},
"vi-VN": {
"select_all": "Chọn tất cả",
"clear_all": "Bỏ chọn",
"selected_count": "Đã chọn {count}",
"install_selected": "Cài đặt mục đã chọn",
"cancel": "Hủy",
"version_label": "Phiên bản",
"file_label": "Tệp",
"repo_label": "Kho",
},
}
def _resolve_language(user_language: str) -> str: def _resolve_language(user_language: str) -> str:
value = str(user_language or "").strip() value = str(user_language or "").strip()
@@ -322,6 +436,19 @@ def _t(lang: str, key: str, **kwargs) -> str:
return text return text
def _selection_t(lang: str, key: str, **kwargs) -> str:
lang_key = _resolve_language(lang)
text = SELECTION_DIALOG_TEXTS.get(
lang_key, SELECTION_DIALOG_TEXTS["en-US"]
).get(key, SELECTION_DIALOG_TEXTS["en-US"][key])
if kwargs:
try:
text = text.format(**kwargs)
except KeyError:
pass
return text
async def _emit_status(emitter: Optional[Any], description: str, done: bool = False) -> None: async def _emit_status(emitter: Optional[Any], description: str, done: bool = False) -> None:
if emitter: if emitter:
await emitter( await emitter(
@@ -738,35 +865,242 @@ def _build_confirmation_hint(lang: str, repo: str, exclude_keywords: str) -> str
return _t(lang, "confirm_copy_exclude_hint", keywords=SELF_EXCLUDE_HINT) return _t(lang, "confirm_copy_exclude_hint", keywords=SELF_EXCLUDE_HINT)
async def _request_confirmation( def _build_selection_dialog_js(
options: List[Dict[str, str]],
ui_text: Dict[str, str],
) -> str:
lines = [
"return new Promise((resolve) => {",
" try {",
f" const options = {json.dumps(options, ensure_ascii=False)};",
f" const ui = {json.dumps(ui_text, ensure_ascii=False)};",
" const dialogId = 'batch-install-plugin-selector';",
" const body = typeof document !== 'undefined' ? document.body : null;",
" const existing = body ? document.getElementById(dialogId) : null;",
" if (existing) { existing.remove(); }",
" if (!body) {",
" resolve({ confirmed: false, error: 'document.body unavailable', selected_ids: [] });",
" return;",
" }",
" const selected = new Set(options.map((item) => item.id));",
" const escapeHtml = (value) => String(value ?? '').replace(/[&<>\"']/g, (char) => ({",
" '&': '&amp;',",
" '<': '&lt;',",
" '>': '&gt;',",
" '\"': '&quot;',",
" \"'\": '&#39;',",
" }[char]));",
" const overlay = document.createElement('div');",
" overlay.id = dialogId;",
" overlay.style.cssText = [",
" 'position:fixed',",
" 'inset:0',",
" 'padding:24px',",
" 'background:rgba(15,23,42,0.52)',",
" 'backdrop-filter:blur(3px)',",
" 'display:flex',",
" 'align-items:center',",
" 'justify-content:center',",
" 'z-index:9999',",
" 'box-sizing:border-box',",
" ].join(';');",
" overlay.innerHTML = `",
" <div style=\"width:min(920px,100%);max-height:min(88vh,900px);overflow:hidden;border-radius:18px;background:#ffffff;box-shadow:0 30px 80px rgba(15,23,42,0.28);display:flex;flex-direction:column;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif\">",
" <div style=\"padding:22px 24px 16px;border-bottom:1px solid #e5e7eb\">",
" <div style=\"display:flex;justify-content:space-between;gap:16px;align-items:flex-start;flex-wrap:wrap\">",
" <div>",
" <div style=\"font-size:22px;font-weight:700;color:#0f172a\">${escapeHtml(ui.title)}</div>",
" <div style=\"margin-top:8px;font-size:14px;color:#475569\">${escapeHtml(ui.list_title)}</div>",
" </div>",
" <div style=\"display:inline-flex;align-items:center;gap:8px;border-radius:999px;background:#eff6ff;color:#1d4ed8;padding:8px 12px;font-size:12px;font-weight:600\">",
" <span>${escapeHtml(ui.repo_label)}</span>",
" <span>${escapeHtml(ui.repo)}</span>",
" </div>",
" </div>",
" <div id=\"batch-install-plugin-selector-hint\" style=\"margin-top:14px;padding:12px 14px;border-radius:12px;background:#f8fafc;color:#334155;font-size:13px;line-height:1.5;white-space:pre-wrap\"></div>",
" </div>",
" <div style=\"padding:16px 24px 0;display:flex;justify-content:space-between;gap:12px;align-items:center;flex-wrap:wrap\">",
" <div style=\"display:flex;gap:8px;flex-wrap:wrap\">",
" <button id=\"batch-install-plugin-selector-select-all\" style=\"padding:8px 12px;border:1px solid #cbd5e1;border-radius:10px;background:#fff;color:#0f172a;font-size:13px;cursor:pointer\">${escapeHtml(ui.select_all)}</button>",
" <button id=\"batch-install-plugin-selector-clear-all\" style=\"padding:8px 12px;border:1px solid #cbd5e1;border-radius:10px;background:#fff;color:#0f172a;font-size:13px;cursor:pointer\">${escapeHtml(ui.clear_all)}</button>",
" </div>",
" <div id=\"batch-install-plugin-selector-count\" style=\"font-size:13px;font-weight:600;color:#475569\"></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>",
" <div style=\"padding:18px 24px 24px;border-top:1px solid #e5e7eb;margin-top:18px;display:flex;justify-content:flex-end;gap:12px;flex-wrap:wrap\">",
" <button id=\"batch-install-plugin-selector-cancel\" style=\"padding:10px 16px;border:1px solid #cbd5e1;border-radius:10px;background:#fff;color:#0f172a;font-weight:600;cursor:pointer\">${escapeHtml(ui.cancel)}</button>",
" <button id=\"batch-install-plugin-selector-submit\" style=\"padding:10px 16px;border:none;border-radius:10px;background:#0f172a;color:#fff;font-weight:600;cursor:pointer\">${escapeHtml(ui.install_selected)}</button>",
" </div>",
" </div>",
" `;",
" body.appendChild(overlay);",
" const previousOverflow = body.style.overflow;",
" body.style.overflow = 'hidden';",
" const listEl = overlay.querySelector('#batch-install-plugin-selector-list');",
" const countEl = overlay.querySelector('#batch-install-plugin-selector-count');",
" const hintEl = overlay.querySelector('#batch-install-plugin-selector-hint');",
" 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');",
" const clearAllBtn = overlay.querySelector('#batch-install-plugin-selector-clear-all');",
" hintEl.textContent = ui.hint || '';",
" hintEl.style.display = ui.hint ? 'block' : 'none';",
" const updateState = () => {",
" countEl.textContent = ui.selected_count.replace('{count}', String(selected.size));",
" submitBtn.disabled = selected.size === 0;",
" submitBtn.style.opacity = selected.size === 0 ? '0.45' : '1';",
" submitBtn.style.cursor = selected.size === 0 ? 'not-allowed' : 'pointer';",
" };",
" const renderList = () => {",
" listEl.innerHTML = options.map((item) => {",
" const checked = selected.has(item.id) ? 'checked' : '';",
" return `",
" <label style=\"display:flex;gap:14px;align-items:flex-start;padding:14px;border:1px solid #e2e8f0;border-radius:14px;background:#fff;cursor:pointer\">",
" <input type=\"checkbox\" data-plugin-id=\"${escapeHtml(item.id)}\" ${checked} style=\"margin-top:3px;width:16px;height:16px;accent-color:#0f172a;flex-shrink:0\" />",
" <div style=\"min-width:0;display:grid;gap:6px\">",
" <div style=\"display:flex;gap:10px;align-items:center;flex-wrap:wrap\">",
" <span style=\"display:inline-flex;align-items:center;padding:2px 8px;border-radius:999px;background:#f1f5f9;color:#334155;font-size:12px;font-weight:700;text-transform:uppercase\">${escapeHtml(item.type)}</span>",
" <span style=\"font-size:15px;font-weight:700;color:#0f172a;word-break:break-word\">${escapeHtml(item.title)}</span>",
" </div>",
" <div style=\"font-size:12px;color:#475569;word-break:break-word\">${escapeHtml(ui.version_label)}: ${escapeHtml(item.version)} · ${escapeHtml(ui.file_label)}: ${escapeHtml(item.file_path)}</div>",
" </div>",
" </label>",
" `;",
" }).join('');",
" listEl.querySelectorAll('input[data-plugin-id]').forEach((input) => {",
" input.addEventListener('change', () => {",
" const pluginId = input.getAttribute('data-plugin-id') || '';",
" if (input.checked) {",
" selected.add(pluginId);",
" } else {",
" selected.delete(pluginId);",
" }",
" updateState();",
" });",
" });",
" updateState();",
" };",
" const cleanup = () => {",
" body.style.overflow = previousOverflow;",
" window.removeEventListener('keydown', onKeyDown, true);",
" overlay.remove();",
" };",
" const finish = (confirmed) => {",
" const selectedIds = confirmed ? options.filter((item) => selected.has(item.id)).map((item) => item.id) : [];",
" cleanup();",
" resolve({ confirmed, selected_ids: selectedIds });",
" };",
" const onKeyDown = (event) => {",
" if (event.key === 'Escape') {",
" event.preventDefault();",
" finish(false);",
" }",
" };",
" overlay.addEventListener('click', (event) => {",
" if (event.target === overlay) {",
" finish(false);",
" }",
" });",
" selectAllBtn.addEventListener('click', () => {",
" options.forEach((item) => selected.add(item.id));",
" renderList();",
" });",
" clearAllBtn.addEventListener('click', () => {",
" selected.clear();",
" renderList();",
" });",
" cancelBtn.addEventListener('click', () => finish(false));",
" submitBtn.addEventListener('click', () => {",
" if (selected.size === 0) {",
" updateState();",
" return;",
" }",
" finish(true);",
" });",
" window.addEventListener('keydown', onKeyDown, true);",
" renderList();",
" } catch (error) {",
" console.error('[Batch Install] Plugin selection dialog failed', error);",
" resolve({",
" confirmed: false,",
" error: error instanceof Error ? error.message : String(error),",
" selected_ids: [],",
" });",
" }",
"});",
]
return "\n".join(lines)
async def _request_plugin_selection(
event_call: Optional[Any], event_call: Optional[Any],
lang: str, lang: str,
message: str, repo: str,
) -> Tuple[bool, Optional[str]]: candidates: List[PluginCandidate],
hint: str,
) -> Tuple[Optional[List[PluginCandidate]], Optional[str]]:
if not event_call: if not event_call:
return True, None return candidates, None
options = [
{
"id": candidate.function_id,
"title": candidate.title,
"type": candidate.plugin_type,
"version": candidate.version,
"file_path": candidate.file_path,
}
for candidate in candidates
]
ui_text = {
"title": _t(lang, "confirm_title"),
"list_title": _t(lang, "status_list_title", count=len(candidates)),
"repo_label": _selection_t(lang, "repo_label"),
"repo": repo,
"hint": hint.strip(),
"select_all": _selection_t(lang, "select_all"),
"clear_all": _selection_t(lang, "clear_all"),
"selected_count": _selection_t(lang, "selected_count", count="{count}"),
"install_selected": _selection_t(lang, "install_selected"),
"cancel": _selection_t(lang, "cancel"),
"version_label": _selection_t(lang, "version_label"),
"file_label": _selection_t(lang, "file_label"),
}
js_code = _build_selection_dialog_js(options, ui_text)
try: try:
confirmed = await asyncio.wait_for( result = await asyncio.wait_for(
event_call( event_call({"type": "execute", "data": {"code": js_code}}),
{
"type": "confirmation",
"data": {
"title": _t(lang, "confirm_title"),
"message": message,
},
}
),
timeout=CONFIRMATION_TIMEOUT, timeout=CONFIRMATION_TIMEOUT,
) )
except asyncio.TimeoutError: except asyncio.TimeoutError:
logger.warning("Installation confirmation timed out.") logger.warning("Installation selection dialog timed out.")
return False, _t(lang, "err_confirm_unavailable") return None, _t(lang, "err_confirm_unavailable")
except Exception as exc: except Exception as exc:
logger.warning("Installation confirmation failed: %s", exc) logger.warning("Installation selection dialog failed: %s", exc)
return False, _t(lang, "err_confirm_unavailable") return None, _t(lang, "err_confirm_unavailable")
return bool(confirmed), None if not isinstance(result, dict):
logger.warning("Unexpected selection dialog result: %r", result)
return None, _t(lang, "err_confirm_unavailable")
if result.get("error"):
logger.warning("Selection dialog returned error: %s", result.get("error"))
return None, _t(lang, "err_confirm_unavailable")
if not result.get("confirmed"):
return [], None
selected_ids = result.get("selected_ids")
if not isinstance(selected_ids, list):
logger.warning("Selection dialog returned invalid selected_ids: %r", selected_ids)
return None, _t(lang, "err_confirm_unavailable")
selected_id_set = {str(item).strip() for item in selected_ids if str(item).strip()}
selected_candidates = [
candidate for candidate in candidates if candidate.function_id in selected_id_set
]
return selected_candidates, None
def parse_github_url(url: str) -> Optional[Tuple[str, str, str]]: def parse_github_url(url: str) -> Optional[Tuple[str, str, str]]:
@@ -1039,24 +1373,15 @@ class Tools:
event_emitter, _t(lang, "err_no_match"), notification_type="warning" event_emitter, _t(lang, "err_no_match"), notification_type="warning"
) )
plugin_list = "\n".join([f"- [{c.plugin_type}] {c.title}" for c in filtered])
hint_msg = _build_confirmation_hint(lang, repo, exclude_keywords) hint_msg = _build_confirmation_hint(lang, repo, exclude_keywords)
confirm_msg = _t( selected_candidates, confirm_error = await _request_plugin_selection(
lang, __event_call__, lang, repo, filtered, hint_msg
"confirm_message",
count=len(filtered),
plugin_list=plugin_list,
hint=hint_msg,
)
confirmed, confirm_error = await _request_confirmation(
__event_call__, lang, confirm_msg
) )
if confirm_error: if confirm_error:
return await _finalize_message( return await _finalize_message(
event_emitter, confirm_error, notification_type="warning" event_emitter, confirm_error, notification_type="warning"
) )
if not confirmed: if not selected_candidates:
return await _finalize_message( return await _finalize_message(
event_emitter, event_emitter,
_t(lang, "confirm_cancelled"), _t(lang, "confirm_cancelled"),
@@ -1070,7 +1395,7 @@ class Tools:
"repo": repo, "repo": repo,
"base_url": base_url, "base_url": base_url,
"note": "Backend uses default port 8080 (containerized environment)", "note": "Backend uses default port 8080 (containerized environment)",
"plugin_count": len(filtered), "plugin_count": len(selected_candidates),
"plugin_types": plugin_types, "plugin_types": plugin_types,
"exclude_keywords": exclude_keywords, "exclude_keywords": exclude_keywords,
"timeout": timeout, "timeout": timeout,
@@ -1092,7 +1417,7 @@ class Tools:
async with httpx.AsyncClient( async with httpx.AsyncClient(
timeout=httpx.Timeout(timeout), follow_redirects=True timeout=httpx.Timeout(timeout), follow_redirects=True
) as client: ) as client:
for candidate in filtered: for candidate in selected_candidates:
await _emit_status( await _emit_status(
event_emitter, event_emitter,
_t( _t(
@@ -1341,12 +1666,17 @@ class Tools:
) )
) )
summary = _t(lang, "status_done", success=success_count, total=len(filtered)) summary = _t(
lang,
"status_done",
success=success_count,
total=len(selected_candidates),
)
output = "\n".join(results + [summary]) output = "\n".join(results + [summary])
notification_type = "success" notification_type = "success"
if success_count == 0: if success_count == 0:
notification_type = "error" notification_type = "error"
elif success_count < len(filtered): elif success_count < len(selected_candidates):
notification_type = "warning" notification_type = "warning"
await _emit_status(event_emitter, summary, done=True) await _emit_status(event_emitter, summary, done=True)

View File

@@ -0,0 +1,33 @@
[![](https://img.shields.io/badge/OpenWebUI%20Community-Get%20Plugin-blue?style=for-the-badge)](https://openwebui.com/t/fujie/batch_install_plugins)
## Overview
Batch Install Plugins from GitHub v1.1.0 upgrades the install confirmation step into an interactive plugin picker powered by the OpenWebUI `execute` event. Users can now review the filtered plugin list in a browser dialog, uncheck anything they do not want, and install only the selected subset.
**[📖 README](https://github.com/Fu-Jie/openwebui-extensions/blob/main/plugins/tools/batch-install-plugins/README.md)**
## Highlights
- **Interactive Selection Dialog**: Opens a checkbox-based browser dialog instead of using the basic confirmation event
- **Selective Installation**: The install loop now runs only for the plugins the user keeps selected
- **Repository Context**: Displays the current repository and exclusion hint inside the dialog
- **Localized UI**: Dialog controls are localized for all supported languages
- **No Workflow Regression**: Existing discovery, filtering, auto-update, and fallback connection logic remain unchanged
## Technical Notes
- Replaced the install confirmation step with `__event_call__({"type": "execute"})`
- Returns a structured payload containing `confirmed` and `selected_ids`
- Preserves the existing 120-second timeout for user interaction
- Keeps installation ordering aligned with the filtered candidate list
## Validation
- Python syntax validated with `python -m py_compile plugins/tools/batch-install-plugins/batch_install_plugins.py`
- README and mirrored docs updated to match the new interactive selection flow
## Upgrade Notes
- No new Valves are required
- Existing prompts continue to work
- Users now get a plugin picker before installation begins

View File

@@ -0,0 +1,33 @@
[![](https://img.shields.io/badge/OpenWebUI%20Community-Get%20Plugin-blue?style=for-the-badge)](https://openwebui.com/t/fujie/batch_install_plugins)
## 概述
Batch Install Plugins from GitHub v1.1.0 将原本的安装确认步骤升级为基于 OpenWebUI `execute` 事件的交互式插件选择器。现在,用户可以先在浏览器对话框中查看过滤后的插件列表,取消勾选不想安装的项,然后只安装最终选中的插件子集。
**[📖 README](https://github.com/Fu-Jie/openwebui-extensions/blob/main/plugins/tools/batch-install-plugins/README_CN.md)**
## 亮点
- **交互式选择对话框**:不再只使用基础 confirmation 事件,而是打开支持复选框的浏览器对话框
- **选择性安装**:安装循环只会处理用户最终保留勾选的插件
- **仓库上下文**:对话框中会显示当前仓库与排除提示
- **本地化 UI**:对话框控件已为所有支持语言提供本地化文本
- **工作流不回退**:原有的插件发现、过滤、自动更新和连接回退逻辑保持不变
## 技术说明
- 使用 `__event_call__({"type": "execute"})` 替换安装确认步骤
- 返回包含 `confirmed``selected_ids` 的结构化结果
- 保留原有的 120 秒用户交互超时时间
- 安装顺序仍与过滤后的候选列表保持一致
## 验证
- 已通过 `python -m py_compile plugins/tools/batch-install-plugins/batch_install_plugins.py` 进行 Python 语法校验
- README 与镜像文档已同步为新的交互式选择流程
## 升级说明
- 不需要新增 Valves
- 现有提示词仍可继续使用
- 安装开始前会新增一个插件选择器步骤