diff --git a/frontend-web/src/components/workspace/settings/skill-settings-page.tsx b/frontend-web/src/components/workspace/settings/skill-settings-page.tsx index 2a011b25..67db57a4 100644 --- a/frontend-web/src/components/workspace/settings/skill-settings-page.tsx +++ b/frontend-web/src/components/workspace/settings/skill-settings-page.tsx @@ -2,6 +2,7 @@ import { AlertCircle, + Anchor, ArchiveIcon, ArrowDownUpIcon, BookmarkIcon, @@ -77,6 +78,7 @@ import { useGenerateSkillNameZh, useInstallSkillUpload, useReconcileCustomSkills, + useSetSkillAlwaysOn, useSetSkillFeatured, useSetSkillNameZh, useSetSkillPinned, @@ -84,7 +86,11 @@ import { useSkills, useValidateSkillUpload, } from "@/core/skills/hooks"; -import { usePublishSquares, useViewMode } from "@/core/system-settings/hooks"; +import { + usePublishSquares, + useSkillCompressionSettings, + useViewMode, +} from "@/core/system-settings/hooks"; import type { Skill } from "@/core/skills/type"; import { formatDateTime } from "@/core/utils/datetime"; import { env } from "@/env"; @@ -181,8 +187,10 @@ function SkillSettingsList({ onClose }: { onClose?: () => void }) { const isAdmin = storedAuth?.system_role === "admin"; // Name + tag filtering is resolved server-side via the /api/skills query. - // Non-admins get their 岗位 (position) visibility applied (scope=mine): a - // position that binds skill tags restricts which skills they can see. + // A 岗位 (position) no longer *restricts* the list — it grants skills into the + // member's 我的技能 (each such skill comes back with ``favorited: true``), so + // the 广场 always shows every visible skill and the 我的 tab includes the + // position-granted ones. ``scope`` is kept for API compatibility only. const { skills, isLoading, error } = useSkills({ search, tagIds, @@ -217,7 +225,9 @@ function SkillSettingsList({ onClose }: { onClose?: () => void }) { if (isBuiltin || !!skill.published) { all.push(skill); } - if (isOwnedByMe) { + // "我的" = skills I own OR skills granted into my collection by my 岗位 + // (position) — mirroring how favorited agents land in 我的智能体. + if (isOwnedByMe || !!skill.favorited) { mine.push(skill); } } @@ -601,6 +611,8 @@ export function SkillRow({ const { data: cardPublishSquares } = usePublishSquares(); const setPinned = useSetSkillPinned(); const setFeatured = useSetSkillFeatured(); + const setAlwaysOn = useSetSkillAlwaysOn(); + const { data: compression } = useSkillCompressionSettings(); const deleteSkill = useDeleteCustomSkill(); const duplicateSkillMutation = useDuplicateSkill(); const [confirmDeleteOpen, setConfirmDeleteOpen] = useState(false); @@ -620,6 +632,17 @@ export function SkillRow({ // Admin can edit/delete/unpublish anyone's skill; non-admin only their own. const canManage = isCustom && (isOwned || isAdmin); const canTogglePublish = canManage && !isBuiltin; + // 常驻(免压缩):内置技能仅管理员可设;自定义技能其所有者或管理员可设(与后端一致)。 + const canSetAlwaysOn = isAdmin || isOwned; + + async function handleToggleAlwaysOn(next: boolean) { + try { + await setAlwaysOn.mutateAsync({ skillName: skill.name, alwaysOn: next }); + toast.success(next ? "已设为常驻(免压缩)" : "已取消常驻"); + } catch (err) { + toast.error(err instanceof Error ? err.message : String(err)); + } + } async function handleTogglePublished(next: boolean) { // Publishing: prompt for which square to publish into (when configured). @@ -932,6 +955,27 @@ export function SkillRow({ /> )} + {compression?.enabled && canSetAlwaysOn && ( + + )} {canTogglePublish && (