feat(extensions): unify extensions endpoint and refresh extensions page UX

- Rename /home/plugins route to /home/extensions and update all sidebar links.
- Add unified GET /api/v1/extensions returning plugins, MCP servers and skills,
  sorted by name; replace the three separate frontend fetches with this single call.
- Migrate the extensions page to shadcn primitives (Tabs/Card/Alert/Badge/Skeleton/
  Switch/Label) and clean up hardcoded color tokens on the extension card.
- Add a localStorage-persisted "Group by type" switch that, when enabled in the
  All Types tab, renders extensions grouped by type with a compact section header.
- Show a spinner while loading and rename the empty-state copy from
  "No plugins installed" to "No extensions installed".
- Rename the "格式 / Formats" filter label to "类型 / Types" across all 8 locales.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Junyan Qin
2026-05-09 23:50:17 +08:00
parent 46a9ed3da6
commit 8ff60c5b98
18 changed files with 756 additions and 551 deletions

View File

@@ -449,6 +449,9 @@ const enUS = {
loading: 'Loading...',
getPluginListError: 'Failed to get plugin list:',
noPluginInstalled: 'No plugins installed',
noExtensionInstalled: 'No extensions installed',
loadingExtensions: 'Loading extensions...',
groupByType: 'Group by type',
pluginConfig: 'Plugin Configuration',
pluginSort: 'Plugin Sort',
pluginSortDescription:
@@ -658,7 +661,7 @@ const enUS = {
deprecatedTooltip:
'Please install the corresponding Knowledge Engine plugin.',
filters: {
allFormats: 'All Formats',
allFormats: 'All Types',
more: 'More',
advancedTitle: 'Advanced Filters',
advancedDescription: 'Filter by extension type',

View File

@@ -462,6 +462,9 @@ const esES = {
loading: 'Cargando...',
getPluginListError: 'Error al obtener la lista de plugins:',
noPluginInstalled: 'No hay plugins instalados',
noExtensionInstalled: 'No hay extensiones instaladas',
loadingExtensions: 'Cargando extensiones...',
groupByType: 'Agrupar por tipo',
pluginConfig: 'Configuración del plugin',
pluginSort: 'Orden de plugins',
pluginSortDescription:
@@ -665,7 +668,7 @@ const esES = {
deprecatedTooltip:
'Por favor, instala el plugin de motor de conocimiento correspondiente.',
filters: {
allFormats: 'Todos los formatos',
allFormats: 'Todos los tipos',
more: 'Más',
advancedTitle: 'Filtros avanzados',
advancedDescription: 'Filtrar por tipo de extensión',

View File

@@ -454,6 +454,9 @@ const jaJP = {
loading: '読み込み中...',
getPluginListError: 'プラグインリストの取得に失敗しました:',
noPluginInstalled: 'プラグインがインストールされていません',
noExtensionInstalled: '拡張機能がインストールされていません',
loadingExtensions: '拡張機能を読み込み中...',
groupByType: '種類でグループ化',
pluginConfig: 'プラグイン設定',
pluginSort: 'プラグインの並び替え',
pluginSortDescription:
@@ -659,7 +662,7 @@ const jaJP = {
noTags: 'タグがありません',
},
filters: {
allFormats: 'すべての形式',
allFormats: 'すべての種類',
more: 'もっと',
advancedTitle: '高度なフィルター',
advancedDescription: '拡張子タイプでフィルター',

View File

@@ -458,6 +458,9 @@ const ruRU = {
loading: 'Загрузка...',
getPluginListError: 'Не удалось получить список плагинов:',
noPluginInstalled: 'Плагины не установлены',
noExtensionInstalled: 'Расширения не установлены',
loadingExtensions: 'Загрузка расширений...',
groupByType: 'Группировать по типу',
pluginConfig: 'Настройка плагина',
pluginSort: 'Порядок плагинов',
pluginSortDescription:
@@ -661,7 +664,7 @@ const ruRU = {
deprecatedTooltip:
'Пожалуйста, установите соответствующий плагин движка знаний.',
filters: {
allFormats: 'Все форматы',
allFormats: 'Все типы',
more: 'Ещё',
advancedTitle: 'Расширенные фильтры',
advancedDescription: 'Фильтр по типу расширения',

View File

@@ -445,6 +445,9 @@ const thTH = {
loading: 'กำลังโหลด...',
getPluginListError: 'ไม่สามารถดึงรายการปลั๊กอินได้:',
noPluginInstalled: 'ยังไม่มีปลั๊กอินที่ติดตั้ง',
noExtensionInstalled: 'ยังไม่มีส่วนขยายที่ติดตั้ง',
loadingExtensions: 'กำลังโหลดส่วนขยาย...',
groupByType: 'จัดกลุ่มตามประเภท',
pluginConfig: 'การกำหนดค่าปลั๊กอิน',
pluginSort: 'เรียงลำดับปลั๊กอิน',
pluginSortDescription:
@@ -642,7 +645,7 @@ const thTH = {
deprecated: 'เลิกใช้แล้ว',
deprecatedTooltip: 'กรุณาติดตั้งปลั๊กอินเครื่องมือความรู้ที่เกี่ยวข้อง',
filters: {
allFormats: 'ทุกรูปแบบ',
allFormats: 'ทุกประเภท',
more: 'เพิ่มเติม',
advancedTitle: 'ตัวกรองขั้นสูง',
advancedDescription: 'กรองตามประเภทส่วนขยาย',

View File

@@ -455,6 +455,9 @@ const viVN = {
loading: 'Đang tải...',
getPluginListError: 'Lấy danh sách plugin thất bại:',
noPluginInstalled: 'Chưa cài đặt plugin nào',
noExtensionInstalled: 'Chưa cài đặt tiện ích mở rộng nào',
loadingExtensions: 'Đang tải tiện ích mở rộng...',
groupByType: 'Nhóm theo loại',
pluginConfig: 'Cấu hình Plugin',
pluginSort: 'Sắp xếp Plugin',
pluginSortDescription:
@@ -655,7 +658,7 @@ const viVN = {
deprecated: 'Không còn hỗ trợ',
deprecatedTooltip: 'Vui lòng cài đặt plugin Công cụ tri thức tương ứng.',
filters: {
allFormats: 'Tất cả định dạng',
allFormats: 'Tất cả loại',
more: 'Thêm',
advancedTitle: 'Bộ lọc nâng cao',
advancedDescription: 'Lọc theo loại phần mở rộng',

View File

@@ -433,6 +433,9 @@ const zhHans = {
getPluginListError: '获取插件列表失败:',
pluginConfig: '插件配置',
noPluginInstalled: '暂未安装任何插件',
noExtensionInstalled: '暂未安装任何扩展',
loadingExtensions: '正在加载扩展...',
groupByType: '按类型分组',
pluginSort: '插件排序',
pluginSortDescription:
'插件顺序会影响同一事件内的处理顺序,请拖动插件卡片排序',
@@ -634,7 +637,7 @@ const zhHans = {
noTags: '暂无标签',
},
filters: {
allFormats: '全部格式',
allFormats: '全部类型',
more: '更多',
advancedTitle: '高级筛选',
advancedDescription: '按扩展类型筛选',

View File

@@ -434,6 +434,9 @@ const zhHant = {
getPluginListError: '取得外掛清單失敗:',
pluginConfig: '外掛設定',
noPluginInstalled: '暫未安裝任何外掛',
noExtensionInstalled: '暫未安裝任何擴充功能',
loadingExtensions: '正在載入擴充功能...',
groupByType: '依類型分組',
pluginSort: '外掛排序',
pluginSortDescription:
'外掛順序會影響同一事件內的處理順序,請拖曳外掛卡片排序',
@@ -627,7 +630,7 @@ const zhHant = {
noTags: '暫無標籤',
},
filters: {
allFormats: '全部格式',
allFormats: '全部類型',
more: '更多',
advancedTitle: '高級篩選',
advancedDescription: '按擴展類型篩選',