diff --git a/crates/agent-gateway/web/src/i18n/config.ts b/crates/agent-gateway/web/src/i18n/config.ts index 9c188b371..efb223b00 100644 --- a/crates/agent-gateway/web/src/i18n/config.ts +++ b/crates/agent-gateway/web/src/i18n/config.ts @@ -76,6 +76,20 @@ export const translations: Record> = { "chat.workspaceResourcesSearch": "搜索名称或描述", "chat.workspaceResourcesSelected": "已选择 {skills} 个 Skills、{mcp} 个 MCP", "chat.workspaceResourcesMissingSkill": "此 Skill 尚未安装在当前设备上。", + "chat.workspaceResourcesInheritNotice": + "这里展示的是全局启用状态,仅供查看。切换到“自定义”后可为当前工作空间单独调整。", + "chat.workspaceResourcesSwitchToCustom": "切换到自定义", + "chat.workspaceResourcesOffStateTitle": "此工作空间已关闭 Skills 与 MCP", + "chat.workspaceResourcesOffStateDescription": + "Agent 不会加载这些扩展资源,但仍可使用文件、终端等基础工具。", + "chat.workspaceResourcesEnabled": "已启用", + "chat.workspaceResourcesDisabled": "未启用", + "chat.workspaceResourcesAlwaysEnabled": "始终启用", + "chat.workspaceResourcesBuiltIn": "内置", + "chat.workspaceResourcesMissing": "未安装", + "chat.workspaceResourcesGloballyDisabled": "全局未启用", + "chat.workspaceResourcesEmptySkills": "没有找到匹配的 Skills", + "chat.workspaceResourcesEmptyMcp": "没有找到匹配的 MCP 服务", "chat.workspacePin": "置顶工作空间", "chat.workspaceUnpin": "取消置顶", "chat.workspaceRename": "修改标题", @@ -2094,6 +2108,21 @@ export const translations: Record> = { "settings.skillsInstalledSortNewest": "安装时间 新→旧", "settings.skillsNoMatch": "没有匹配「{filter}」的技能", "settings.skillsStoreSearch": "搜索 ClawHub 技能", + "settings.skillsStoreCategoryAll": "全部", + "settings.skillsStoreCategoryIntegrations": "集成", + "settings.skillsStoreCategoryAutomation": "自动化", + "settings.skillsStoreCategoryResearch": "研究", + "settings.skillsStoreCategoryDevelopment": "开发", + "settings.skillsStoreCategoryProductivity": "效率", + "settings.skillsStoreCategoryCommunication": "沟通", + "settings.skillsStoreCategoryCreative": "创意", + "settings.skillsStoreCategoryKnowledge": "知识", + "settings.skillsStoreCategoryAgents": "智能体", + "settings.skillsStoreCategoryOperations": "运维", + "settings.skillsStoreCategorySecurity": "安全", + "settings.skillsStoreCategoryFinance": "金融", + "settings.skillsStoreCategoryLifestyle": "生活", + "settings.skillsStoreCategoryOther": "其他", "settings.skillsStoreSortMostDownloaded": "下载最多", "settings.skillsStoreSortMostStarred": "星标最多", "settings.skillsStoreSortMostInstalled": "安装最多", @@ -2273,6 +2302,20 @@ export const translations: Record> = { "chat.workspaceResourcesSearch": "Search names or descriptions", "chat.workspaceResourcesSelected": "{skills} Skills and {mcp} MCP servers selected", "chat.workspaceResourcesMissingSkill": "This Skill is not installed on this device.", + "chat.workspaceResourcesInheritNotice": + "These global states are shown for reference. Switch to Custom to configure this workspace separately.", + "chat.workspaceResourcesSwitchToCustom": "Switch to Custom", + "chat.workspaceResourcesOffStateTitle": "Skills and MCP are disabled for this workspace", + "chat.workspaceResourcesOffStateDescription": + "Agents will not load these extensions, but core tools such as files and terminal remain available.", + "chat.workspaceResourcesEnabled": "Enabled", + "chat.workspaceResourcesDisabled": "Disabled", + "chat.workspaceResourcesAlwaysEnabled": "Always enabled", + "chat.workspaceResourcesBuiltIn": "Built-in", + "chat.workspaceResourcesMissing": "Not installed", + "chat.workspaceResourcesGloballyDisabled": "Disabled globally", + "chat.workspaceResourcesEmptySkills": "No matching Skills found", + "chat.workspaceResourcesEmptyMcp": "No matching MCP servers found", "chat.workspacePin": "Pin workspace", "chat.workspaceUnpin": "Unpin", "chat.workspaceRename": "Rename", @@ -4381,6 +4424,21 @@ export const translations: Record> = { "settings.skillsInstalledSortNewest": "Installation time: newest first", "settings.skillsNoMatch": 'No skills matching "{filter}"', "settings.skillsStoreSearch": "Search ClawHub Skills", + "settings.skillsStoreCategoryAll": "All", + "settings.skillsStoreCategoryIntegrations": "Integrations", + "settings.skillsStoreCategoryAutomation": "Automation", + "settings.skillsStoreCategoryResearch": "Research", + "settings.skillsStoreCategoryDevelopment": "Development", + "settings.skillsStoreCategoryProductivity": "Productivity", + "settings.skillsStoreCategoryCommunication": "Communication", + "settings.skillsStoreCategoryCreative": "Creative", + "settings.skillsStoreCategoryKnowledge": "Knowledge", + "settings.skillsStoreCategoryAgents": "Agents", + "settings.skillsStoreCategoryOperations": "Operations", + "settings.skillsStoreCategorySecurity": "Security", + "settings.skillsStoreCategoryFinance": "Finance", + "settings.skillsStoreCategoryLifestyle": "Lifestyle", + "settings.skillsStoreCategoryOther": "Other", "settings.skillsStoreSortMostDownloaded": "Most Downloaded", "settings.skillsStoreSortMostStarred": "Most Starred", "settings.skillsStoreSortMostInstalled": "Most Installed", diff --git a/crates/agent-gui/src/i18n/config.ts b/crates/agent-gui/src/i18n/config.ts index 76b8db689..ea7f5c8f2 100644 --- a/crates/agent-gui/src/i18n/config.ts +++ b/crates/agent-gui/src/i18n/config.ts @@ -94,6 +94,20 @@ export const translations: Record> = { "chat.workspaceResourcesSearch": "搜索名称或描述", "chat.workspaceResourcesSelected": "已选择 {skills} 个 Skills、{mcp} 个 MCP", "chat.workspaceResourcesMissingSkill": "此 Skill 尚未安装在当前设备上。", + "chat.workspaceResourcesInheritNotice": + "这里展示的是全局启用状态,仅供查看。切换到“自定义”后可为当前工作空间单独调整。", + "chat.workspaceResourcesSwitchToCustom": "切换到自定义", + "chat.workspaceResourcesOffStateTitle": "此工作空间已关闭 Skills 与 MCP", + "chat.workspaceResourcesOffStateDescription": + "Agent 不会加载这些扩展资源,但仍可使用文件、终端等基础工具。", + "chat.workspaceResourcesEnabled": "已启用", + "chat.workspaceResourcesDisabled": "未启用", + "chat.workspaceResourcesAlwaysEnabled": "始终启用", + "chat.workspaceResourcesBuiltIn": "内置", + "chat.workspaceResourcesMissing": "未安装", + "chat.workspaceResourcesGloballyDisabled": "全局未启用", + "chat.workspaceResourcesEmptySkills": "没有找到匹配的 Skills", + "chat.workspaceResourcesEmptyMcp": "没有找到匹配的 MCP 服务", "chat.workspacePin": "置顶工作空间", "chat.workspaceUnpin": "取消置顶", "chat.workspaceRename": "修改标题", @@ -2376,6 +2390,20 @@ export const translations: Record> = { "chat.workspaceResourcesSearch": "Search names or descriptions", "chat.workspaceResourcesSelected": "{skills} Skills and {mcp} MCP servers selected", "chat.workspaceResourcesMissingSkill": "This Skill is not installed on this device.", + "chat.workspaceResourcesInheritNotice": + "These global states are shown for reference. Switch to Custom to configure this workspace separately.", + "chat.workspaceResourcesSwitchToCustom": "Switch to Custom", + "chat.workspaceResourcesOffStateTitle": "Skills and MCP are disabled for this workspace", + "chat.workspaceResourcesOffStateDescription": + "Agents will not load these extensions, but core tools such as files and terminal remain available.", + "chat.workspaceResourcesEnabled": "Enabled", + "chat.workspaceResourcesDisabled": "Disabled", + "chat.workspaceResourcesAlwaysEnabled": "Always enabled", + "chat.workspaceResourcesBuiltIn": "Built-in", + "chat.workspaceResourcesMissing": "Not installed", + "chat.workspaceResourcesGloballyDisabled": "Disabled globally", + "chat.workspaceResourcesEmptySkills": "No matching Skills found", + "chat.workspaceResourcesEmptyMcp": "No matching MCP servers found", "chat.workspacePin": "Pin workspace", "chat.workspaceUnpin": "Unpin", "chat.workspaceRename": "Rename", diff --git a/crates/agent-ui/src/components/chat/WorkspaceResourceSettingsDrawer.tsx b/crates/agent-ui/src/components/chat/WorkspaceResourceSettingsDrawer.tsx index 1b8e9a1c5..389394de8 100644 --- a/crates/agent-ui/src/components/chat/WorkspaceResourceSettingsDrawer.tsx +++ b/crates/agent-ui/src/components/chat/WorkspaceResourceSettingsDrawer.tsx @@ -1,6 +1,16 @@ -import { useEffect, useMemo, useState } from "react"; -import { createPortal } from "react-dom"; -import { Blend, Cable, Search, X } from "@liveagent/app/components/icons"; +import { + Ban, + Blend, + Cable, + Check, + Folder, + Globe2, + Info, + Lock, + Search, + Settings2, + X, +} from "@liveagent/app/components/icons"; import { type AppSettings, type WorkspaceProject, @@ -9,17 +19,45 @@ import { } from "@liveagent/app/lib/settings"; import { useLocale } from "@liveagent/ui/i18n/index"; import { cn } from "@liveagent/ui/lib/shared/utils"; -import { isAlwaysEnabledSkillName, type SkillSummary } from "@liveagent/ui/lib/skills/index"; import { CLAWHUB_CATEGORY_SLUGS, type ClawHubCategorySlug, classifyClawHubSkill, } from "@liveagent/ui/lib/skills/clawHubCategories"; +import { isAlwaysEnabledSkillName, type SkillSummary } from "@liveagent/ui/lib/skills/index"; +import { useEffect, useMemo, useState } from "react"; +import { createPortal } from "react-dom"; import { ResourceActivationSwitch } from "../resources/ResourceActivationSwitch"; import { Button } from "../ui/button"; +import { RadioGroup, RadioGroupItem } from "../ui/radio-group"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "../ui/tabs"; +import { ToggleGroup, ToggleGroupItem } from "../ui/toggle-group"; type ResourceTab = "skills" | "mcp"; type SkillCategory = "all" | ClawHubCategorySlug; +const WORKSPACE_RESOURCE_MODES = ["inherit", "custom", "off"] as const; +const RESOURCE_TABS = ["skills", "mcp"] as const; + +function ResourceStatePill(props: { active: boolean; label: string }) { + return ( + + + {props.label} + + ); +} function classifySkill(skill: Pick): ClawHubCategorySlug[] { if (isAlwaysEnabledSkillName(skill.name)) return ["other"]; @@ -120,6 +158,176 @@ export function WorkspaceResourceSettingsDrawer(props: { const readonly = mode !== "custom"; const visibleSkillSelection = mode === "inherit" ? globalSkillNames : skillNames; const visibleMcpSelection = mode === "inherit" ? globalMcpIds : mcpServerIds; + const selectedSkillCount = settings.skills.enabled + ? listedSkills.filter( + ({ skill }) => + isAlwaysEnabledSkillName(skill.name) || visibleSkillSelection.has(skill.name), + ).length + : 0; + const selectedMcpCount = settings.mcp.servers.filter( + (server) => server.enabled && visibleMcpSelection.has(server.id), + ).length; + const modeContent = { + inherit: { + icon: Globe2, + label: t("chat.workspaceResourcesModeInherit"), + description: t("chat.workspaceResourcesInheritHint"), + }, + custom: { + icon: Settings2, + label: t("chat.workspaceResourcesModeCustom"), + description: t("chat.workspaceResourcesCustomHint"), + }, + off: { + icon: Ban, + label: t("chat.workspaceResourcesModeOff"), + description: t("chat.workspaceResourcesOffHint"), + }, + }; + const modeOptions = WORKSPACE_RESOURCE_MODES.map((value) => ({ + value, + ...modeContent[value], + })); + const currentModeLabel = + modeOptions.find((option) => option.value === mode)?.label ?? + t("chat.workspaceResourcesModeInherit"); + const skillList = + filteredSkills.length > 0 ? ( + filteredSkills.map(({ skill, missing }, index) => { + const alwaysEnabled = isAlwaysEnabledSkillName(skill.name); + const checked = + settings.skills.enabled && (alwaysEnabled || visibleSkillSelection.has(skill.name)); + return ( +
0 && "border-t border-border/45", + mode === "custom" && "hover:bg-muted/20", + )} + > +
+
+ {skill.name} + {alwaysEnabled ? ( + + + {t("chat.workspaceResourcesBuiltIn")} + + ) : null} + {missing ? ( + + {t("chat.workspaceResourcesMissing")} + + ) : null} +
+
+ {skill.description} +
+
+ {readonly ? ( + + ) : alwaysEnabled ? ( + + ) : ( + { + const value = new Set(skillNames); + if (next) value.add(skill.name); + else value.delete(skill.name); + setSkillNames(value); + }} + /> + )} +
+ ); + }) + ) : ( +
+ +

+ {t("chat.workspaceResourcesEmptySkills")} +

+
+ ); + const mcpList = + filteredMcp.length > 0 ? ( + filteredMcp.map((server, index) => { + const checked = visibleMcpSelection.has(server.id) && server.enabled; + return ( +
0 && "border-t border-border/45", + mode === "custom" && "hover:bg-muted/20", + )} + > +
+
+ {server.id} + + {server.transport} + + {!server.enabled ? ( + + {t("chat.workspaceResourcesGloballyDisabled")} + + ) : null} +
+
+ {server.command || server.url || t("mcpHub.statusEmptyDesc")} +
+
+ {readonly ? ( + + ) : ( + { + const value = new Set(mcpServerIds); + if (next) value.add(server.id); + else value.delete(server.id); + setMcpServerIds(value); + }} + /> + )} +
+ ); + }) + ) : ( +
+ +

{t("chat.workspaceResourcesEmptyMcp")}

+
+ ); return createPortal(
@@ -129,197 +337,231 @@ export function WorkspaceResourceSettingsDrawer(props: { aria-label={t("window.close")} onClick={onClose} /> -