mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-06-26 15:34:26 +00:00
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:
@@ -34,7 +34,7 @@ import {
|
||||
RefreshCcw,
|
||||
Bot,
|
||||
Workflow,
|
||||
Group,
|
||||
ListTree,
|
||||
} from 'lucide-react';
|
||||
import { useTheme } from '@/components/providers/theme-provider';
|
||||
|
||||
@@ -1061,42 +1061,69 @@ function NavItems({
|
||||
<span className="cursor-pointer select-none">
|
||||
{config.name}
|
||||
</span>
|
||||
<div className="ml-auto flex items-center gap-0.5 -mr-1">
|
||||
{isAgents && (
|
||||
<button
|
||||
type="button"
|
||||
title={t('agents.groupByKind')}
|
||||
className={cn(
|
||||
'p-1 rounded-sm transition-all',
|
||||
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
|
||||
{/* Group/refresh controls — left-aligned, hugging the title */}
|
||||
{(isAgents || isExtensionsCategory) && (
|
||||
<div className="flex items-center gap-0.5">
|
||||
{isAgents && (
|
||||
<button
|
||||
type="button"
|
||||
title={t('agents.groupByKind')}
|
||||
className={cn(
|
||||
'size-3.5',
|
||||
extRefreshing && 'animate-spin',
|
||||
'flex items-center gap-1 px-1.5 py-1 rounded-sm text-[10px] transition-all',
|
||||
sidebarData.agentsGroupByKind
|
||||
? 'text-sidebar-accent-foreground bg-sidebar-accent'
|
||||
: 'text-sidebar-foreground/70 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground',
|
||||
)}
|
||||
/>
|
||||
</button>
|
||||
)}
|
||||
onClick={(e) => {
|
||||
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 &&
|
||||
(isPlugin ? (
|
||||
<DropdownMenu>
|
||||
|
||||
@@ -520,6 +520,7 @@ const enUS = {
|
||||
kindBadgeAgent: 'Agent',
|
||||
kindBadgePipeline: 'Pipeline',
|
||||
groupByKind: 'Group by type',
|
||||
groupByKindShort: 'Group',
|
||||
pipelineTypeDescription:
|
||||
'Keep the existing no-code message pipeline for backward compatibility. It only handles message events.',
|
||||
allEvents: 'Supports all EBA events',
|
||||
@@ -582,6 +583,7 @@ const enUS = {
|
||||
noExtensionInstalled: 'No extensions installed',
|
||||
loadingExtensions: 'Loading extensions...',
|
||||
groupByType: 'Group by format',
|
||||
groupByTypeShort: 'Group',
|
||||
pluginConfig: 'Plugin Configuration',
|
||||
pluginSort: 'Plugin Sort',
|
||||
pluginSortDescription:
|
||||
|
||||
@@ -486,6 +486,7 @@ const esES = {
|
||||
noExtensionInstalled: 'No hay extensiones instaladas',
|
||||
loadingExtensions: 'Cargando extensiones...',
|
||||
groupByType: 'Agrupar por formato',
|
||||
groupByTypeShort: 'Agrupar',
|
||||
pluginConfig: 'Configuración del plugin',
|
||||
pluginSort: 'Orden de plugins',
|
||||
pluginSortDescription:
|
||||
|
||||
@@ -504,6 +504,7 @@ const jaJP = {
|
||||
kindBadgeAgent: 'Agent',
|
||||
kindBadgePipeline: 'パイプライン',
|
||||
groupByKind: 'タイプ別にグループ化',
|
||||
groupByKindShort: 'グループ',
|
||||
pipelineTypeDescription:
|
||||
'既存のノーコードメッセージ Pipeline を互換性のため保持します。メッセージイベントのみ処理できます。',
|
||||
allEvents: 'すべての EBA イベントに対応',
|
||||
@@ -564,6 +565,7 @@ const jaJP = {
|
||||
noExtensionInstalled: '拡張機能がインストールされていません',
|
||||
loadingExtensions: '拡張機能を読み込み中...',
|
||||
groupByType: '形式でグループ化',
|
||||
groupByTypeShort: 'グループ',
|
||||
pluginConfig: 'プラグイン設定',
|
||||
pluginSort: 'プラグインの並び替え',
|
||||
pluginSortDescription:
|
||||
|
||||
@@ -485,6 +485,7 @@ const ruRU = {
|
||||
noExtensionInstalled: 'Расширения не установлены',
|
||||
loadingExtensions: 'Загрузка расширений...',
|
||||
groupByType: 'Группировать по формату',
|
||||
groupByTypeShort: 'Группа',
|
||||
pluginConfig: 'Настройка плагина',
|
||||
pluginSort: 'Порядок плагинов',
|
||||
pluginSortDescription:
|
||||
|
||||
@@ -470,6 +470,7 @@ const thTH = {
|
||||
noExtensionInstalled: 'ยังไม่มีส่วนขยายที่ติดตั้ง',
|
||||
loadingExtensions: 'กำลังโหลดส่วนขยาย...',
|
||||
groupByType: 'จัดกลุ่มตามรูปแบบ',
|
||||
groupByTypeShort: 'จัดกลุ่ม',
|
||||
pluginConfig: 'การกำหนดค่าปลั๊กอิน',
|
||||
pluginSort: 'เรียงลำดับปลั๊กอิน',
|
||||
pluginSortDescription:
|
||||
|
||||
@@ -480,6 +480,7 @@ const viVN = {
|
||||
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 định dạng',
|
||||
groupByTypeShort: 'Nhóm',
|
||||
pluginConfig: 'Cấu hình Plugin',
|
||||
pluginSort: 'Sắp xếp Plugin',
|
||||
pluginSortDescription:
|
||||
|
||||
@@ -500,6 +500,7 @@ const zhHans = {
|
||||
kindBadgeAgent: 'Agent',
|
||||
kindBadgePipeline: '流水线',
|
||||
groupByKind: '按类型分组',
|
||||
groupByKindShort: '分组',
|
||||
pipelineTypeDescription:
|
||||
'保留现有无代码消息流水线,兼容旧配置,只能处理消息事件。',
|
||||
allEvents: '支持全部 EBA 事件',
|
||||
@@ -559,6 +560,7 @@ const zhHans = {
|
||||
noExtensionInstalled: '暂未安装任何扩展',
|
||||
loadingExtensions: '正在加载扩展...',
|
||||
groupByType: '按格式分组',
|
||||
groupByTypeShort: '分组',
|
||||
pluginSort: '插件排序',
|
||||
pluginSortDescription:
|
||||
'插件顺序会影响同一事件内的处理顺序,请拖动插件卡片排序',
|
||||
|
||||
@@ -457,6 +457,7 @@ const zhHant = {
|
||||
noExtensionInstalled: '暫未安裝任何擴充功能',
|
||||
loadingExtensions: '正在載入擴充功能...',
|
||||
groupByType: '依格式分組',
|
||||
groupByTypeShort: '分組',
|
||||
pluginSort: '外掛排序',
|
||||
pluginSortDescription:
|
||||
'外掛順序會影響同一事件內的處理順序,請拖曳外掛卡片排序',
|
||||
|
||||
Reference in New Issue
Block a user