From b200e5b91b742c37b746db4eeee32019a18c8075 Mon Sep 17 00:00:00 2001 From: dong <2970752254@qq.com> Date: Tue, 22 Sep 2026 11:44:43 +0800 Subject: [PATCH] fix(frontend): show empty skill export requirements (#5659) --- .../capabilities/skill-export-dialog.tsx | 23 ++++++--- frontend/src/core/i18n/locales/en-US.ts | 1 + frontend/src/core/i18n/locales/types.ts | 1 + frontend/src/core/i18n/locales/zh-CN.ts | 1 + .../skill-export-dialog.dom.test.tsx | 49 +++++++++++++++++++ 5 files changed, 67 insertions(+), 8 deletions(-) diff --git a/frontend/src/components/workspace/capabilities/skill-export-dialog.tsx b/frontend/src/components/workspace/capabilities/skill-export-dialog.tsx index 91b034543..48c7d3c18 100644 --- a/frontend/src/components/workspace/capabilities/skill-export-dialog.tsx +++ b/frontend/src/components/workspace/capabilities/skill-export-dialog.tsx @@ -217,8 +217,11 @@ export default function SkillExportDialog({ {text.exportTools}
- {manifest.requirements.allowed_tools?.join(", ") ?? - text.exportUndeclared} + {manifest.requirements.allowed_tools === null + ? text.exportUndeclared + : manifest.requirements.allowed_tools.length === 0 + ? text.exportNone + : manifest.requirements.allowed_tools.join(", ")}
@@ -226,12 +229,16 @@ export default function SkillExportDialog({ {text.exportSecrets}
- {manifest.requirements.required_secrets - ?.map( - (secret) => - `${secret.name} (${secret.optional ? text.exportOptional : text.exportRequired})`, - ) - .join(", ") ?? text.exportUndeclared} + {manifest.requirements.required_secrets === null + ? text.exportUndeclared + : manifest.requirements.required_secrets.length === 0 + ? text.exportNone + : manifest.requirements.required_secrets + .map( + (secret) => + `${secret.name} (${secret.optional ? text.exportOptional : text.exportRequired})`, + ) + .join(", ")}
diff --git a/frontend/src/core/i18n/locales/en-US.ts b/frontend/src/core/i18n/locales/en-US.ts index 93cc4e056..1834bbacd 100644 --- a/frontend/src/core/i18n/locales/en-US.ts +++ b/frontend/src/core/i18n/locales/en-US.ts @@ -1792,6 +1792,7 @@ export const enUS: Translations = { exportOptional: "optional", exportRequired: "required", exportUndeclared: "Not declared", + exportNone: "None", exportScope: "Includes all files inside this skill. Account settings, conversations and history outside the skill folder are excluded. Configure tools and credentials again on the destination.", exportWarnings: "Check package contents", diff --git a/frontend/src/core/i18n/locales/types.ts b/frontend/src/core/i18n/locales/types.ts index b2f0960bb..ca7fa52f4 100644 --- a/frontend/src/core/i18n/locales/types.ts +++ b/frontend/src/core/i18n/locales/types.ts @@ -1434,6 +1434,7 @@ export interface Translations { exportOptional: string; exportRequired: string; exportUndeclared: string; + exportNone: string; exportScope: string; exportWarnings: string; exportWarningDescription: string; diff --git a/frontend/src/core/i18n/locales/zh-CN.ts b/frontend/src/core/i18n/locales/zh-CN.ts index e3e964338..0763b697a 100644 --- a/frontend/src/core/i18n/locales/zh-CN.ts +++ b/frontend/src/core/i18n/locales/zh-CN.ts @@ -1683,6 +1683,7 @@ export const zhCN: Translations = { exportOptional: "可选", exportRequired: "必需", exportUndeclared: "未声明", + exportNone: "无", exportScope: "包含此技能目录内的全部文件。账号配置、对话和目录外的历史不会导出;目标环境需重新配置工具与凭据。", exportWarnings: "请检查包内文件", diff --git a/frontend/tests/unit/components/workspace/capabilities/skill-export-dialog.dom.test.tsx b/frontend/tests/unit/components/workspace/capabilities/skill-export-dialog.dom.test.tsx index 6346f0340..09f4350df 100644 --- a/frontend/tests/unit/components/workspace/capabilities/skill-export-dialog.dom.test.tsx +++ b/frontend/tests/unit/components/workspace/capabilities/skill-export-dialog.dom.test.tsx @@ -66,6 +66,55 @@ beforeEach(() => { afterEach(cleanup); describe("export dialog lifecycle", () => { + it.each([ + { + name: "undeclared", + allowedTools: null, + requiredSecrets: null, + expectedTools: "Not declared", + expectedSecrets: "Not declared", + }, + { + name: "explicitly empty", + allowedTools: [], + requiredSecrets: [], + expectedTools: "None", + expectedSecrets: "None", + }, + { + name: "declared values", + allowedTools: ["read_file"], + requiredSecrets: [{ name: "API_KEY", optional: false }], + expectedTools: "read_file", + expectedSecrets: "API_KEY (required)", + }, + ])( + "renders $name requirements", + async ({ + allowedTools, + requiredSecrets, + expectedTools, + expectedSecrets, + }) => { + mocks.load.mockResolvedValue({ + ...manifest, + requirements: { + ...manifest.requirements, + allowed_tools: allowedTools, + required_secrets: requiredSecrets, + }, + }); + render(); + await screen.findByText("Declared requirements"); + expect( + screen.getByText("Allowed tools").nextElementSibling?.textContent, + ).toBe(expectedTools); + expect( + screen.getByText("Credential names").nextElementSibling?.textContent, + ).toBe(expectedSecrets); + }, + ); + it("pages the file list and distinguishes undeclared dependencies", async () => { render(); await screen.findByText("DEMO_KEY (optional)");