diff --git a/apps/web/src/components/settings/SkillsSettings.tsx b/apps/web/src/components/settings/SkillsSettings.tsx index f6e28f845999..65efcef891c7 100644 --- a/apps/web/src/components/settings/SkillsSettings.tsx +++ b/apps/web/src/components/settings/SkillsSettings.tsx @@ -1,5 +1,12 @@ import { normalizeSkillVisibilityPath } from "@t3tools/client-runtime/providerSkills"; -import { CopyIcon, EyeIcon, FolderOpenIcon, RefreshCwIcon } from "lucide-react"; +import { + CopyIcon, + EyeIcon, + FolderOpenIcon, + PlusIcon, + SearchIcon, + TriangleAlertIcon, +} from "lucide-react"; import { useEffect, useState } from "react"; import { useProjectFileQuery } from "../files/projectFilesQueryState"; @@ -12,9 +19,11 @@ import { useAtomCommand } from "../../state/use-atom-command"; import { Badge } from "../ui/badge"; import { Button } from "../ui/button"; import { Dialog, DialogHeader, DialogPopup, DialogTitle } from "../ui/dialog"; -import { Input } from "../ui/input"; +import { InputGroup, InputGroupAddon, InputGroupInput } from "../ui/input-group"; +import { RefreshIcon } from "../ui/refresh-icon"; import { toastManager } from "../ui/toast"; -import { SettingsPageContainer } from "./settingsLayout"; +import { Tooltip, TooltipPopup, TooltipTrigger } from "../ui/tooltip"; +import { SettingsPageContainer, SettingsSection } from "./settingsLayout"; import { useSettingsScope } from "./SettingsScopeContext"; import { discoverLocalSkillFiles, @@ -109,158 +118,188 @@ export function SkillsSettings() { return ( -
-

Skills

-

- File skills in your home folders and selected projects. -

-
-
- - {filtered.length} {filtered.length === 1 ? "skill" : "skills"} - - setQuery(event.currentTarget.value)} - placeholder="Filter" - aria-label="Filter skills" - className="min-w-0 flex-1" - /> - - -
- {errors.length > 0 && ( -

- Some skills could not be loaded. {errors.join("; ")} -

- )} -

- Switches show or hide skills in this client's T3 skill pickers. Providers manage automatic - skill discovery. -

-
+ + + {filtered.length} {filtered.length === 1 ? "skill" : "skills"} + + + + + + setQuery(event.currentTarget.value)} + onKeyDown={(event) => { + if (event.key === "Escape") setQuery(""); + }} + placeholder="Search skills" + aria-label="Search skills" + /> + + + setRevision((value) => value + 1)} + > + + + } + /> + Refresh skills + + + setAdding(true)} + > + + + } + /> + Add skill + +
+ } + > + {errors.map((error) => ( +

+ + {error} +

+ ))} {filtered.length === 0 ? (

{refreshing ? "Loading skills..." : normalizedQuery ? "No matching skills" - : "No file skills found for this selection"} + : "No skills in your home folders or selected projects"}

) : ( - filtered.map((skill) => ( -
-
- - - {skill.scope === "project" ? "Project" : "Personal"} - - - {skill.source} - - - (hiddenByEnvironment[skill.environmentId] ?? []).some( - (hidden) => - normalizeSkillVisibilityPath(hidden) === - normalizeSkillVisibilityPath(alias), - ), - ) - } - aria-label={`Show ${skill.name} in skill pickers`} - onCheckedChange={(checked) => - setVisible(skill.environmentId, skill.aliases, checked) - } - /> -
- {skill.description ? ( -

{skill.description}

- ) : null} -
-

- {skill.path} -

- {multipleEnvironments ? ( - - {skill.environmentLabel} + filtered.map((skill) => { + const visible = !skill.aliases.some((alias) => + (hiddenByEnvironment[skill.environmentId] ?? []).some( + (hidden) => + normalizeSkillVisibilityPath(hidden) === normalizeSkillVisibilityPath(alias), + ), + ); + return ( +
+
+ + + {skill.scope === "project" ? "Project" : "Personal"} + + + {skill.source} + + + setVisible(skill.environmentId, skill.aliases, checked) + } + /> + } + /> + + {visible ? "Shown in skill pickers" : "Hidden from skill pickers"} + + +
+ {skill.description ? ( +

{skill.description}

) : null} - - - {revealableEnvironments.has(skill.environmentId) ? ( +
+

+ {skill.path} +

+ {multipleEnvironments ? ( + + {skill.environmentLabel} + + ) : null} + - ) : null} + {revealableEnvironments.has(skill.environmentId) ? ( + + ) : null} +
-
- )) + ); + }) )} -
+ {adding && (