feat(sidebar): add group-by-type toggle to installed extensions section

- Add group-by-type button to extensions category header (mirrors the
  agent group-by-kind pattern) — syncs with the extensions page Switch
  via shared SidebarDataContext state
- Relocate both group and refresh controls to sit left-aligned
  immediately after the title for both Agent and Extensions sections
- Add plugins.groupByTypeShort i18n key to all 8 locales

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Junyan Qin
2026-06-26 21:42:02 +08:00
committed by huanghuoguoguo
parent 1a3599eb7a
commit 0027ea8f68
9 changed files with 73 additions and 35 deletions
@@ -34,7 +34,7 @@ import {
RefreshCcw, RefreshCcw,
Bot, Bot,
Workflow, Workflow,
Group, ListTree,
} from 'lucide-react'; } from 'lucide-react';
import { useTheme } from '@/components/providers/theme-provider'; import { useTheme } from '@/components/providers/theme-provider';
@@ -1112,42 +1112,69 @@ function NavItems({
<span className="cursor-pointer select-none"> <span className="cursor-pointer select-none">
{config.name} {config.name}
</span> </span>
<div className="ml-auto flex items-center gap-0.5 -mr-1"> {/* Group/refresh controls — left-aligned, hugging the title */}
{isAgents && ( {(isAgents || isExtensionsCategory) && (
<button <div className="flex items-center gap-0.5">
type="button" {isAgents && (
title={t('agents.groupByKind')} <button
className={cn( type="button"
'p-1 rounded-sm transition-all', title={t('agents.groupByKind')}
sidebarData.agentsGroupByKind
? 'text-sidebar-accent-foreground bg-sidebar-accent'
: 'text-sidebar-foreground/70 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground [@media(hover:hover)]:opacity-0 group-hover/category-header:opacity-100',
)}
onClick={(e) => {
e.stopPropagation();
sidebarData.setAgentsGroupByKind(
!sidebarData.agentsGroupByKind,
);
}}
>
<Group className="size-3.5" />
</button>
)}
{isExtensionsCategory && (
<button
type="button"
title={t('common.refresh', '刷新')}
className="p-1 rounded-sm text-sidebar-foreground/70 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground [@media(hover:hover)]:opacity-0 group-hover/category-header:opacity-100 transition-all"
onClick={handleRefreshExtensions}
>
<RefreshCcw
className={cn( className={cn(
'size-3.5', 'flex items-center gap-1 px-1.5 py-1 rounded-sm text-[10px] transition-all',
extRefreshing && 'animate-spin', sidebarData.agentsGroupByKind
? 'text-sidebar-accent-foreground bg-sidebar-accent'
: 'text-sidebar-foreground/70 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground',
)} )}
/> onClick={(e) => {
</button> e.stopPropagation();
)} sidebarData.setAgentsGroupByKind(
!sidebarData.agentsGroupByKind,
);
}}
>
<ListTree className="size-3.5" />
<span>{t('agents.groupByKindShort')}</span>
</button>
)}
{isExtensionsCategory && (
<button
type="button"
title={t('plugins.groupByType')}
className={cn(
'flex items-center gap-1 px-1.5 py-1 rounded-sm text-[10px] transition-all',
sidebarData.extensionsGroupByType
? 'text-sidebar-accent-foreground bg-sidebar-accent'
: 'text-sidebar-foreground/70 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground',
)}
onClick={(e) => {
e.stopPropagation();
sidebarData.setExtensionsGroupByType(
!sidebarData.extensionsGroupByType,
);
}}
>
<ListTree className="size-3.5" />
<span>{t('plugins.groupByTypeShort')}</span>
</button>
)}
{isExtensionsCategory && (
<button
type="button"
title={t('common.refresh', '刷新')}
className="p-1 rounded-sm text-sidebar-foreground/70 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground [@media(hover:hover)]:opacity-0 group-hover/category-header:opacity-100 transition-all"
onClick={handleRefreshExtensions}
>
<RefreshCcw
className={cn(
'size-3.5',
extRefreshing && 'animate-spin',
)}
/>
</button>
)}
</div>
)}
<div className="ml-auto flex items-center gap-0.5 -mr-1">
{canCreate && {canCreate &&
(isPlugin ? ( (isPlugin ? (
<DropdownMenu> <DropdownMenu>
+2
View File
@@ -540,6 +540,7 @@ const enUS = {
kindBadgeAgent: 'Agent', kindBadgeAgent: 'Agent',
kindBadgePipeline: 'Pipeline', kindBadgePipeline: 'Pipeline',
groupByKind: 'Group by type', groupByKind: 'Group by type',
groupByKindShort: 'Group',
pipelineTypeDescription: pipelineTypeDescription:
'Keep the existing no-code message pipeline for backward compatibility. It only handles message events.', 'Keep the existing no-code message pipeline for backward compatibility. It only handles message events.',
allEvents: 'Supports all EBA events', allEvents: 'Supports all EBA events',
@@ -602,6 +603,7 @@ const enUS = {
noExtensionInstalled: 'No extensions installed', noExtensionInstalled: 'No extensions installed',
loadingExtensions: 'Loading extensions...', loadingExtensions: 'Loading extensions...',
groupByType: 'Group by format', groupByType: 'Group by format',
groupByTypeShort: 'Group',
pluginConfig: 'Plugin Configuration', pluginConfig: 'Plugin Configuration',
pluginSort: 'Plugin Sort', pluginSort: 'Plugin Sort',
pluginSortDescription: pluginSortDescription:
+1
View File
@@ -506,6 +506,7 @@ const esES = {
noExtensionInstalled: 'No hay extensiones instaladas', noExtensionInstalled: 'No hay extensiones instaladas',
loadingExtensions: 'Cargando extensiones...', loadingExtensions: 'Cargando extensiones...',
groupByType: 'Agrupar por formato', groupByType: 'Agrupar por formato',
groupByTypeShort: 'Agrupar',
pluginConfig: 'Configuración del plugin', pluginConfig: 'Configuración del plugin',
pluginSort: 'Orden de plugins', pluginSort: 'Orden de plugins',
pluginSortDescription: pluginSortDescription:
+2
View File
@@ -524,6 +524,7 @@ const jaJP = {
kindBadgeAgent: 'Agent', kindBadgeAgent: 'Agent',
kindBadgePipeline: 'パイプライン', kindBadgePipeline: 'パイプライン',
groupByKind: 'タイプ別にグループ化', groupByKind: 'タイプ別にグループ化',
groupByKindShort: 'グループ',
pipelineTypeDescription: pipelineTypeDescription:
'既存のノーコードメッセージ Pipeline を互換性のため保持します。メッセージイベントのみ処理できます。', '既存のノーコードメッセージ Pipeline を互換性のため保持します。メッセージイベントのみ処理できます。',
allEvents: 'すべての EBA イベントに対応', allEvents: 'すべての EBA イベントに対応',
@@ -584,6 +585,7 @@ const jaJP = {
noExtensionInstalled: '拡張機能がインストールされていません', noExtensionInstalled: '拡張機能がインストールされていません',
loadingExtensions: '拡張機能を読み込み中...', loadingExtensions: '拡張機能を読み込み中...',
groupByType: '形式でグループ化', groupByType: '形式でグループ化',
groupByTypeShort: 'グループ',
pluginConfig: 'プラグイン設定', pluginConfig: 'プラグイン設定',
pluginSort: 'プラグインの並び替え', pluginSort: 'プラグインの並び替え',
pluginSortDescription: pluginSortDescription:
+1
View File
@@ -505,6 +505,7 @@ const ruRU = {
noExtensionInstalled: 'Расширения не установлены', noExtensionInstalled: 'Расширения не установлены',
loadingExtensions: 'Загрузка расширений...', loadingExtensions: 'Загрузка расширений...',
groupByType: 'Группировать по формату', groupByType: 'Группировать по формату',
groupByTypeShort: 'Группа',
pluginConfig: 'Настройка плагина', pluginConfig: 'Настройка плагина',
pluginSort: 'Порядок плагинов', pluginSort: 'Порядок плагинов',
pluginSortDescription: pluginSortDescription:
+1
View File
@@ -490,6 +490,7 @@ const thTH = {
noExtensionInstalled: 'ยังไม่มีส่วนขยายที่ติดตั้ง', noExtensionInstalled: 'ยังไม่มีส่วนขยายที่ติดตั้ง',
loadingExtensions: 'กำลังโหลดส่วนขยาย...', loadingExtensions: 'กำลังโหลดส่วนขยาย...',
groupByType: 'จัดกลุ่มตามรูปแบบ', groupByType: 'จัดกลุ่มตามรูปแบบ',
groupByTypeShort: 'จัดกลุ่ม',
pluginConfig: 'การกำหนดค่าปลั๊กอิน', pluginConfig: 'การกำหนดค่าปลั๊กอิน',
pluginSort: 'เรียงลำดับปลั๊กอิน', pluginSort: 'เรียงลำดับปลั๊กอิน',
pluginSortDescription: pluginSortDescription:
+1
View File
@@ -500,6 +500,7 @@ const viVN = {
noExtensionInstalled: 'Chưa cài đặt tiện ích mở rộng 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...', loadingExtensions: 'Đang tải tiện ích mở rộng...',
groupByType: 'Nhóm theo định dạng', groupByType: 'Nhóm theo định dạng',
groupByTypeShort: 'Nhóm',
pluginConfig: 'Cấu hình Plugin', pluginConfig: 'Cấu hình Plugin',
pluginSort: 'Sắp xếp Plugin', pluginSort: 'Sắp xếp Plugin',
pluginSortDescription: pluginSortDescription:
+2
View File
@@ -519,6 +519,7 @@ const zhHans = {
kindBadgeAgent: 'Agent', kindBadgeAgent: 'Agent',
kindBadgePipeline: '流水线', kindBadgePipeline: '流水线',
groupByKind: '按类型分组', groupByKind: '按类型分组',
groupByKindShort: '分组',
pipelineTypeDescription: pipelineTypeDescription:
'保留现有无代码消息流水线,兼容旧配置,只能处理消息事件。', '保留现有无代码消息流水线,兼容旧配置,只能处理消息事件。',
allEvents: '支持全部 EBA 事件', allEvents: '支持全部 EBA 事件',
@@ -578,6 +579,7 @@ const zhHans = {
noExtensionInstalled: '暂未安装任何扩展', noExtensionInstalled: '暂未安装任何扩展',
loadingExtensions: '正在加载扩展...', loadingExtensions: '正在加载扩展...',
groupByType: '按格式分组', groupByType: '按格式分组',
groupByTypeShort: '分组',
pluginSort: '插件排序', pluginSort: '插件排序',
pluginSortDescription: pluginSortDescription:
'插件顺序会影响同一事件内的处理顺序,请拖动插件卡片排序', '插件顺序会影响同一事件内的处理顺序,请拖动插件卡片排序',
+1
View File
@@ -476,6 +476,7 @@ const zhHant = {
noExtensionInstalled: '暫未安裝任何擴充功能', noExtensionInstalled: '暫未安裝任何擴充功能',
loadingExtensions: '正在載入擴充功能...', loadingExtensions: '正在載入擴充功能...',
groupByType: '依格式分組', groupByType: '依格式分組',
groupByTypeShort: '分組',
pluginSort: '外掛排序', pluginSort: '外掛排序',
pluginSortDescription: pluginSortDescription:
'外掛順序會影響同一事件內的處理順序,請拖曳外掛卡片排序', '外掛順序會影響同一事件內的處理順序,請拖曳外掛卡片排序',