(skillId: string, btn: HTMLButtonElement)
| 70 | } |
| 71 | |
| 72 | async function downloadSkill(skillId: string, btn: HTMLButtonElement): Promise<void> { |
| 73 | const skill = allItems.find((item) => item.id === skillId); |
| 74 | if (!skill || !skill.files || skill.files.length === 0) { |
| 75 | showToast('No files found for this skill.', 'error'); |
| 76 | return; |
| 77 | } |
| 78 | |
| 79 | const originalContent = btn.innerHTML; |
| 80 | btn.disabled = true; |
| 81 | btn.innerHTML = '<svg class="spinner" viewBox="0 0 16 16" width="16" height="16" fill="currentColor"><path d="M8 0a8 8 0 1 0 8 8h-1.5A6.5 6.5 0 1 1 8 1.5V0z"/></svg> Preparing...'; |
| 82 | |
| 83 | try { |
| 84 | await downloadZipBundle(skill.id, skill.files); |
| 85 | |
| 86 | btn.innerHTML = '<svg viewBox="0 0 16 16" width="16" height="16" fill="currentColor"><path d="M13.78 4.22a.75.75 0 0 1 0 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L2.22 9.28a.75.75 0 0 1 1.06-1.06L6 10.94l6.72-6.72a.75.75 0 0 1 1.06 0z"/></svg> Downloaded!'; |
| 87 | setTimeout(() => { |
| 88 | btn.disabled = false; |
| 89 | btn.innerHTML = originalContent; |
| 90 | }, 2000); |
| 91 | } catch (error) { |
| 92 | const message = error instanceof Error ? error.message : 'Download failed.'; |
| 93 | showToast(message, 'error'); |
| 94 | btn.innerHTML = '<svg viewBox="0 0 16 16" width="16" height="16" fill="currentColor"><path d="M3.72 3.72a.75.75 0 0 1 1.06 0L8 6.94l3.22-3.22a.75.75 0 1 1 1.06 1.06L9.06 8l3.22 3.22a.75.75 0 0 1-1.06 1.06L8 9.06l-3.22 3.22a.75.75 0 0 1-1.06-1.06L6.94 8 3.72 4.78a.75.75 0 0 1 0-1.06z"/></svg> Failed'; |
| 95 | setTimeout(() => { |
| 96 | btn.disabled = false; |
| 97 | btn.innerHTML = originalContent; |
| 98 | }, 2000); |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | function openSkillDetailsModal(skillId: string, trigger?: HTMLElement): void { |
| 103 | const item = skillById.get(skillId); |
no test coverage detected