reorder skills for app integrations

This commit is contained in:
Timothy Carambat
2026-04-14 14:25:36 -07:00
parent 1c0d0301b0
commit f17337fb97
4 changed files with 95 additions and 18 deletions

View File

@@ -133,7 +133,7 @@ export default function GMailSkillPanel({
a: (
<Link
className="text-sky-400 hover:text-sky-500 text-xs font-medium underline"
to={paths.docs("/features/gmail-agent")}
to={paths.docs("/agent/usage/gmail-agent")}
target="_blank"
/>
),

View File

@@ -225,7 +225,7 @@ export default function OutlookSkillPanel({
a: (
<Link
className="text-sky-400 hover:text-sky-500 text-xs font-medium underline"
to={paths.docs("/features/outlook-agent")}
to={paths.docs("/agent/usage/outlook-agent")}
target="_blank"
/>
),

View File

@@ -13,11 +13,16 @@ import {
Robot,
Hammer,
FlowArrow,
Package,
} from "@phosphor-icons/react";
import ContextualSaveBar from "@/components/ContextualSaveBar";
import { castToType } from "@/utils/types";
import { FullScreenLoader } from "@/components/Preloader";
import { getDefaultSkills, getConfigurableSkills } from "./skills";
import {
getDefaultSkills,
getConfigurableSkills,
getAppIntegrationSkills,
} from "./skills.jsx";
import { DefaultBadge } from "./Badges/default";
import ImportedSkillList from "./Imported/SkillList";
import ImportedSkillConfig from "./Imported/ImportedSkillConfig";
@@ -68,20 +73,25 @@ export default function AdminAgents() {
fileSystemAgentAvailable,
createFilesAgentAvailable,
});
const allAppIntegrationSkills = getAppIntegrationSkills(t);
// Filter skills based on mode restrictions
// singleUserOnly -> hidden in multi-user mode
// multiUserOnly -> hidden when NOT in multi-user mode
const isMultiUserMode = settings?.MultiUserMode ?? false;
const filterSkillsByMode = ([_, skillConfig]) => {
if (!skillConfig.mode) return true;
if (skillConfig.mode.includes("singleUserOnly") && isMultiUserMode)
return false;
if (skillConfig.mode.includes("multiUserOnly") && !isMultiUserMode)
return false;
return true;
};
const configurableSkills = Object.fromEntries(
Object.entries(allConfigurableSkills).filter(([_, skillConfig]) => {
if (!skillConfig.mode) return true;
if (skillConfig.mode.includes("singleUserOnly") && isMultiUserMode)
return false;
if (skillConfig.mode.includes("multiUserOnly") && !isMultiUserMode)
return false;
return true;
})
Object.entries(allConfigurableSkills).filter(filterSkillsByMode)
);
const appIntegrationSkills = Object.fromEntries(
Object.entries(allAppIntegrationSkills).filter(filterSkillsByMode)
);
// Alert user if they try to leave the page with unsaved changes
@@ -232,6 +242,8 @@ export default function AdminAgents() {
SelectedSkillComponent = ImportedSkillConfig;
} else if (configurableSkills[selectedSkill]) {
SelectedSkillComponent = configurableSkills[selectedSkill]?.component;
} else if (appIntegrationSkills[selectedSkill]) {
SelectedSkillComponent = appIntegrationSkills[selectedSkill]?.component;
} else {
SelectedSkillComponent = defaultSkills[selectedSkill]?.component;
}
@@ -382,6 +394,17 @@ export default function AdminAgents() {
activeSkills={agentSkills}
/>
<div className="text-theme-text-primary flex items-center gap-x-2 mt-6">
<Package size={24} />
<p className="text-lg font-medium">App Integrations</p>
</div>
<SkillList
skills={appIntegrationSkills}
selectedSkill={selectedSkill}
handleClick={handleSkillClick}
activeSkills={agentSkills}
/>
<div className="text-theme-text-primary flex items-center gap-x-2">
<Plug size={24} />
<p className="text-lg font-medium">Custom Skills</p>
@@ -484,7 +507,7 @@ export default function AdminAgents() {
setHasChanges={setHasChanges}
{...defaultSkills[selectedSkill]}
/>
) : (
) : configurableSkills?.[selectedSkill] ? (
// The selected skill is a configurable skill - show the configurable skill panel
<SelectedSkillComponent
skill={configurableSkills[selectedSkill]?.skill}
@@ -497,6 +520,21 @@ export default function AdminAgents() {
hasChanges={hasChanges}
{...configurableSkills[selectedSkill]}
/>
) : (
// The selected skill is an app integration skill
<SelectedSkillComponent
skill={
appIntegrationSkills[selectedSkill]?.skill
}
settings={settings}
toggleSkill={toggleAgentSkill}
enabled={agentSkills.includes(
appIntegrationSkills[selectedSkill]?.skill
)}
setHasChanges={setHasChanges}
hasChanges={hasChanges}
{...appIntegrationSkills[selectedSkill]}
/>
)}
</>
)}
@@ -580,6 +618,17 @@ export default function AdminAgents() {
activeSkills={agentSkills}
/>
<div className="text-theme-text-primary flex items-center gap-x-2 mt-6">
<Package size={24} />
<p className="text-lg font-medium">App Integrations</p>
</div>
<SkillList
skills={appIntegrationSkills}
selectedSkill={selectedSkill}
handleClick={handleSkillClick}
activeSkills={agentSkills}
/>
<div className="text-theme-text-primary flex items-center gap-x-2 mt-4">
<Plug size={24} />
<p className="text-lg font-medium">Custom Skills</p>
@@ -680,7 +729,7 @@ export default function AdminAgents() {
setHasChanges={setHasChanges}
{...defaultSkills[selectedSkill]}
/>
) : (
) : configurableSkills?.[selectedSkill] ? (
// The selected skill is a configurable skill - show the configurable skill panel
<SelectedSkillComponent
skill={configurableSkills[selectedSkill]?.skill}
@@ -693,6 +742,19 @@ export default function AdminAgents() {
hasChanges={hasChanges}
{...configurableSkills[selectedSkill]}
/>
) : (
// The selected skill is an app integration skill
<SelectedSkillComponent
skill={appIntegrationSkills[selectedSkill]?.skill}
settings={settings}
toggleSkill={toggleAgentSkill}
enabled={agentSkills.includes(
appIntegrationSkills[selectedSkill]?.skill
)}
setHasChanges={setHasChanges}
hasChanges={hasChanges}
{...appIntegrationSkills[selectedSkill]}
/>
)}
</>
)}
@@ -740,6 +802,7 @@ function SkillList({
selectedSkill = null,
handleClick = null,
activeSkills = [],
Icon = null,
}) {
if (skills.length === 0) return null;
@@ -766,7 +829,14 @@ function SkillList({
}`}
onClick={() => handleClick?.(skill)}
>
<div className="text-sm font-light">{settings.title}</div>
<div className="flex items-center gap-x-2">
{settings.Icon ? (
<settings.Icon size={16} />
) : (
Icon && <Icon size={16} />
)}
<div className="text-sm font-light">{settings.title}</div>
</div>
<div className="flex items-center gap-x-2">
{isDefault ? (
<DefaultBadge title={skill} />

View File

@@ -13,8 +13,6 @@ import {
ChartBar,
FolderOpen,
FilePlus,
EnvelopeSimple,
MicrosoftOutlookLogo,
} from "@phosphor-icons/react";
import RAGImage from "@/media/agents/rag-memory.png";
import SummarizeImage from "@/media/agents/view-summarize.png";
@@ -22,6 +20,8 @@ import ScrapeWebsitesImage from "@/media/agents/scrape-websites.png";
import GenerateChartsImage from "@/media/agents/generate-charts.png";
import GenerateSaveImages from "@/media/agents/generate-save-files.png";
import FileSystemImage from "@/media/agents/file-system.png";
import GMailIcon from "./GMailSkillPanel/gmail.png";
import OutlookIcon from "./OutlookSkillPanel/outlook.png";
export const getDefaultSkills = (t) => ({
"rag-memory": {
@@ -94,12 +94,17 @@ export const getConfigurableSkills = (
component: AgentSQLConnectorSelection,
skill: "sql-agent",
},
});
export const getAppIntegrationSkills = (t) => ({
"gmail-agent": {
title: t("agent.skill.gmail.title"),
description: t("agent.skill.gmail.description"),
component: GMailSkillPanel,
skill: "gmail-agent",
icon: EnvelopeSimple,
Icon: ({ size }) => (
<img src={GMailIcon} alt="GMail" width={size} height={size} />
),
mode: ["singleUserOnly"],
},
"outlook-agent": {
@@ -107,7 +112,9 @@ export const getConfigurableSkills = (
description: t("agent.skill.outlook.description"),
component: OutlookSkillPanel,
skill: "outlook-agent",
icon: MicrosoftOutlookLogo,
Icon: ({ size }) => (
<img src={OutlookIcon} alt="Outlook" width={size} height={size} />
),
mode: ["singleUserOnly"],
},
});