diff --git a/src/renderer/src/lib/icons/icon-collections.generated.ts b/src/renderer/src/lib/icons/icon-collections.generated.ts index fd445afb6..2b6b9bc6d 100644 --- a/src/renderer/src/lib/icons/icon-collections.generated.ts +++ b/src/renderer/src/lib/icons/icon-collections.generated.ts @@ -209,6 +209,9 @@ export const lucideIconCollection = { copy: { body: '' }, + 'copy-plus': { + body: '' + }, 'corner-down-left': { body: '' }, @@ -254,6 +257,9 @@ export const lucideIconCollection = { file: { body: '' }, + 'file-archive': { + body: '' + }, 'file-code': { body: '' }, diff --git a/src/renderer/src/lib/icons/icon-whitelist.generated.ts b/src/renderer/src/lib/icons/icon-whitelist.generated.ts index 74f21149e..7fc52dc13 100644 --- a/src/renderer/src/lib/icons/icon-whitelist.generated.ts +++ b/src/renderer/src/lib/icons/icon-whitelist.generated.ts @@ -76,6 +76,7 @@ export const GENERATED_ICON_WHITELIST: Record(null) const activeView = ref('skills') const searchQuery = ref('') const externalImportOpen = ref(false) +const installDialogOpen = ref(false) +const gitDialogOpen = ref(false) const detailDialogOpen = ref(false) const selectedSkill = ref(null) const skillDetail = ref(null) @@ -366,15 +376,38 @@ onUnmounted(() => { {{ t('settings.skills.syncDirectory.action') }} - - - {{ t('settings.skills.agentImport.menuItem') }} - + + + + + {{ t('settings.skills.addSkill') }} + + + + + + + + @@ -453,6 +486,8 @@ onUnmounted(() => { :agents="agents" @imported="loadPage(true)" /> + + + + + + {{ t('settings.skills.git.title') }} + + {{ t('settings.skills.git.description') }} + + + + + + + + + + {{ t('settings.skills.git.scan') }} + + + + + {{ t('settings.skills.git.failed') }} + + {{ t('common.error.requestFailed') }} + + + + + + + + + {{ t('settings.skills.git.detectedFormat') }} + {{ + t(`settings.skills.git.format.${scanResult.repoFormat}`) + }} + + + {{ t('settings.skills.git.selectedCount', { count: selectedNames.size }) }} + + + + + + {{ t('settings.skills.git.empty') }} + + + + + + + {{ skill.name }} + + + {{ t('settings.skills.git.conflict') }} + + + {{ t('settings.skills.git.invalid') }} + + + + {{ skill.valid ? skill.description : t('settings.skills.git.invalid') }} + + + {{ skill.relativePath }} + + + + + + + {{ t('settings.skills.git.strategy') }} + + + + {{ t('settings.skills.git.rename') }} + + + + {{ t('settings.skills.git.overwrite') }} + + + + {{ t('settings.skills.git.skip') }} + + + + + + + + + + + + + + diff --git a/src/renderer/src/pages/plugins/skills/SkillInstallDialog.vue b/src/renderer/src/pages/plugins/skills/SkillInstallDialog.vue new file mode 100644 index 000000000..61cc6900c --- /dev/null +++ b/src/renderer/src/pages/plugins/skills/SkillInstallDialog.vue @@ -0,0 +1,497 @@ + + + + + {{ t('settings.skills.install.title') }} + + {{ t('settings.skills.install.description') }} + + + + + + + + {{ t('settings.skills.install.tabFolder') }} + + + + {{ t('settings.skills.install.tabZip') }} + + + + {{ t('settings.skills.install.tabUrl') }} + + + + + + + + + {{ t('settings.skills.install.folderHint') }} + + + + {{ t('settings.skills.install.folderTip') }} + + + + + + + + + {{ t('settings.skills.install.zipHint') }} + + + + + + + + + {{ t('settings.skills.install.urlHint') }} + + + + {{ t('settings.skills.install.installButton') }} + + + + + + {{ validationError }} + + + + + + + + + + diff --git a/test/renderer/components/SkillsPluginsPage.test.ts b/test/renderer/components/SkillsPluginsPage.test.ts index a40a1eb82..310a34868 100644 --- a/test/renderer/components/SkillsPluginsPage.test.ts +++ b/test/renderer/components/SkillsPluginsPage.test.ts @@ -60,6 +60,36 @@ const passthrough = (name: string) => template: '' }) +const DropdownActionItemStub = defineComponent({ + name: 'DcDropdownActionItem', + inheritAttrs: false, + props: { icon: String, label: String, disabled: Boolean }, + emits: ['select'], + template: + '{{ label }}' +}) + +const ImportSkillsFromAgentDialogStub = defineComponent({ + name: 'ImportSkillsFromAgentDialog', + props: { open: Boolean }, + emits: ['update:open', 'imported'], + template: '' +}) + +const SkillInstallDialogStub = defineComponent({ + name: 'SkillInstallDialog', + props: { open: Boolean }, + emits: ['update:open'], + template: '' +}) + +const InstallFromGitDialogStub = defineComponent({ + name: 'InstallFromGitDialog', + props: { open: Boolean }, + emits: ['update:open'], + template: '' +}) + const deferred = () => { let resolve!: (value: T) => void const promise = new Promise((nextResolve) => { @@ -164,7 +194,13 @@ const mountSkillsPluginsPage = async () => { template: '' }), - ImportSkillsFromAgentDialog: true, + DropdownMenu: passthrough('DropdownMenu'), + DropdownMenuTrigger: passthrough('DropdownMenuTrigger'), + DropdownMenuContent: passthrough('DropdownMenuContent'), + DcDropdownActionItem: DropdownActionItemStub, + ImportSkillsFromAgentDialog: ImportSkillsFromAgentDialogStub, + SkillInstallDialog: SkillInstallDialogStub, + InstallFromGitDialog: InstallFromGitDialogStub, SkillDetailDialog: SkillDetailDialogStub, Icon: true } @@ -316,4 +352,35 @@ describe('SkillsPluginsPage', () => { expect(mocks.skillClient.deleteSkill).toHaveBeenCalledWith('review', ['agent-a', 'agent-b']) }) + + it('exposes manual Skill import entries from the Add Skill menu', async () => { + const wrapper = await mountSkillsPluginsPage() + await flushPromises() + + expect(wrapper.get('[data-testid="skills-import-action"]')).toBeTruthy() + expect(wrapper.get('[data-testid="skills-import-from-agent"]').text()).toContain( + 'settings.skills.agentImport.menuItem' + ) + expect(wrapper.get('[data-testid="skills-import-basic"]').text()).toContain( + 'settings.skills.install.basicTitle' + ) + expect(wrapper.get('[data-testid="skills-import-git"]').text()).toContain( + 'settings.skills.git.menuItem' + ) + + await wrapper.get('[data-testid="skills-import-basic"]').trigger('click') + expect(wrapper.get('[data-testid="install-dialog"]').attributes('data-open')).toBe('true') + wrapper.findComponent(SkillInstallDialogStub).vm.$emit('update:open', false) + await flushPromises() + expect(wrapper.get('[data-testid="install-dialog"]').attributes('data-open')).toBe('false') + + await wrapper.get('[data-testid="skills-import-git"]').trigger('click') + expect(wrapper.get('[data-testid="git-install-dialog"]').attributes('data-open')).toBe('true') + wrapper.findComponent(InstallFromGitDialogStub).vm.$emit('update:open', false) + await flushPromises() + expect(wrapper.get('[data-testid="git-install-dialog"]').attributes('data-open')).toBe('false') + + await wrapper.get('[data-testid="skills-import-from-agent"]').trigger('click') + expect(wrapper.get('[data-testid="agent-import-dialog"]').attributes('data-open')).toBe('true') + }) })
+ {{ t('settings.skills.install.folderHint') }} +
+ {{ t('settings.skills.install.folderTip') }} +
+ {{ t('settings.skills.install.zipHint') }} +
+ {{ t('settings.skills.install.urlHint') }} +