您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds download and install buttons to VS Code extension marketplace pages. Download .vsix files directly or install extensions to VS Code, Cursor, Windsurf, Trae, and Kiro with one click.
// ==UserScript== // @name 更好的 VS Code 扩展安装程序 // @name:en Better VS Code Extension Installer // @namespace http://tampermonkey.net/ // @version 1.13 // @description Adds download and install buttons to VS Code extension marketplace pages. Download .vsix files directly or install extensions to VS Code, Cursor, Windsurf, Trae, and Kiro with one click. // @description:en Adds download and install buttons to VS Code extension marketplace pages. Download .vsix files directly or install extensions to VS Code, Cursor, Windsurf, Trae, and Kiro with one click. // @description:zh-CN 为 VS Code 扩展市场页面添加下载和安装按钮。直接下载 .vsix 文件或一键安装扩展到 VS Code、Cursor、Windsurf、Trae 和 Kiro。 // @author eachann1024 // @match https://marketplace.visualstudio.com/items?itemName=* // @icon https://www.google.com/s2/favicons?sz=64&domain=visualstudio.com // @grant none // @license MIT // @homepageURL https://github.com/eachann1024/scripts-toolbox // @supportURL https://github.com/eachann1024/scripts-toolbox/issues // @run-at document-idle // @compatible chrome // @compatible firefox // @compatible edge // @category Developer Tools // @tag vscode // @tag extension // @tag download // @tag cursor // @tag windsurf // @tag marketplace // @tag vsix // @tag 开发工具 // @tag vscode扩展 // @tag 扩展 // @tag 下载 // @tag Kiro // @tag Trae // @tag Windsurf // @keyword vscode extension download cursor windsurf trae kiro vsix marketplace // @keyword 扩展下载 插件下载 开发工具 // ==/UserScript== (function() { 'use strict'; function getItemNameFromURL() { const urlParams = new URLSearchParams(window.location.search); return urlParams.get('itemName'); } function parseItemName(itemName) { const parts = itemName.split('.'); if (parts.length < 2) { throw new Error('Invalid itemName format'); } return { publisher: parts[0], extensionName: parts.slice(1).join('.') }; } function findVersion() { const detailsSections = document.querySelectorAll('.additional-details-section'); for (let section of detailsSections) { const header = section.querySelector('h3'); if (header && header.textContent.trim() === 'Additional Details') { const rows = section.querySelectorAll('tr'); for (let row of rows) { const firstCell = row.querySelector('td:first-child'); if (firstCell && firstCell.textContent.trim() === 'Version') { const versionCell = row.querySelector('td:last-child'); if (versionCell) { return versionCell.textContent.trim(); } } } } } const versionHistory = document.querySelector('#version-history'); if (versionHistory) { const versionElement = versionHistory.querySelector('tr:nth-child(2) td:nth-child(1)'); if (versionElement) { return versionElement.textContent.trim(); } } const versionElement = document.querySelector('.version, [data-reporting-key="Version"]'); if (versionElement) { return versionElement.textContent.trim(); } const jsonScript = document.querySelector('script.jiContent'); if (jsonScript) { try { const jsonData = JSON.parse(jsonScript.textContent); if (jsonData.Resources && jsonData.Resources.Version) { return jsonData.Resources.Version; } } catch (e) { console.error('Error parsing JSON from script tag:', e); } } throw new Error('Could not find extension version on the page'); } function downloadVSIX(publisher, extensionName, version) { const downloadUrl = `https://marketplace.visualstudio.com/_apis/public/gallery/publishers/${publisher}/vsextensions/${extensionName}/${version}/vspackage`; const link = document.createElement('a'); link.href = downloadUrl; link.download = `${extensionName}-${version}.vsix`; document.body.appendChild(link); link.click(); document.body.removeChild(link); } function addCustomInstallButtons() { try { const itemName = getItemNameFromURL(); if (!itemName) { console.error('Item name not found in URL'); return; } const { publisher, extensionName } = parseItemName(itemName); let installContainer = document.querySelector('.ux-item-action'); if (!installContainer) { installContainer = document.querySelector('.install-button-container') || document.querySelector('.extension-action-container') || document.querySelector('[class*="install"][class*="button"]') || document.querySelector('.action-buttons'); } if (!installContainer) { console.error('Main install button container not found'); return; } const customButtonsContainer = document.createElement('div'); customButtonsContainer.className = 'custom-install-buttons'; customButtonsContainer.style.marginTop = '10px'; customButtonsContainer.style.display = 'flex'; customButtonsContainer.style.gap = '10px'; customButtonsContainer.style.flexWrap = 'wrap'; const editors = [ { name: 'Cursor', scheme: 'cursor', iconUrl: 'https://icon.horse/icon/cursor.sh' }, { name: 'Windsurf', scheme: 'windsurf', iconUrl: 'https://icon.horse/icon/windsurf.ai' }, { name: 'Trae', scheme: 'trae', iconUrl: 'https://lf-cdn.trae.ai/obj/trae-ai-sg/trae_website_prod/favicon.png' }, { name: 'Kiro', scheme: 'kiro', iconUrl: 'https://kiro.dev/favicon.ico' } ]; const downloadButtonWrapper = document.createElement('span'); downloadButtonWrapper.className = 'ux-oneclick-install-button-container'; downloadButtonWrapper.innerHTML = ` <button type="button" class="ms-Button ux-button install ms-Button--default root-39" data-is-focusable="true" id="download-extension-button"> <div class="ms-Button-flexContainer flexContainer-40" style="display: flex; align-items: center;"> <div class="ms-Button-textContainer textContainer-41"> <div class="ms-Button-label label-43" style="display: flex; align-items: center;"> <img src="https://icon.horse/icon/visualstudio.com" alt="Download icon" style="width: 16px; height: 16px; margin-right: 8px; display: inline-block;"> Download VSIX </div> </div> </div> </button> `; const downloadButton = downloadButtonWrapper.querySelector('button'); downloadButton.addEventListener('click', function(e) { e.preventDefault(); e.stopPropagation(); try { const version = findVersion(); downloadVSIX(publisher, extensionName, version); } catch (error) { console.error('Error downloading extension:', error); alert('Failed to download extension. Please check console for details.'); } }); customButtonsContainer.appendChild(downloadButtonWrapper); editors.forEach(editor => { const buttonWrapper = document.createElement('span'); buttonWrapper.className = 'ux-oneclick-install-button-container'; buttonWrapper.innerHTML = ` <button type="button" class="ms-Button ux-button install ms-Button--default root-39" data-is-focusable="true"> <div class="ms-Button-flexContainer flexContainer-40" style="display: flex; align-items: center;"> <div class="ms-Button-textContainer textContainer-41"> <div class="ms-Button-label label-43" style="display: flex; align-items: center;"> <img src="${editor.iconUrl}" alt="${editor.name} icon" style="width: 16px; height: 16px; margin-right: 8px; display: inline-block;"> ${editor.name} </div> </div> </div> </button> `; const customButton = buttonWrapper.querySelector('button'); customButton.addEventListener('click', function(e) { e.preventDefault(); e.stopPropagation(); try { const customUrl = `${editor.scheme}:extension/${publisher}.${extensionName}`; window.open(customUrl, '_self'); } catch (error) { console.error(`Error installing to ${editor.name}:`, error); alert(`Failed to install to ${editor.name}. Please check console for details.`); } }); customButtonsContainer.appendChild(buttonWrapper); }); installContainer.parentNode.insertBefore(customButtonsContainer, installContainer.nextSibling); addVersionHistoryButtons(editors, publisher, extensionName); } catch (error) { console.error('Error adding custom install buttons:', error); } } function addVersionHistoryButtons(editors, publisher, extensionName) { const targetTable = document.querySelector('#version-history table') || document.querySelector('.version-history-table, [class*="version"][class*="history"] table'); if (!targetTable) { return; } const rows = targetTable.querySelectorAll('tbody tr'); rows.forEach(row => { if (row.querySelector('.version-custom-buttons')) { return; } const versionCell = row.querySelector('td:first-child'); if (!versionCell) return; const version = versionCell.textContent.trim(); const buttonsContainer = document.createElement('td'); buttonsContainer.className = 'version-custom-buttons'; buttonsContainer.style.cssText = 'padding: 8px; white-space: nowrap; text-align: center;'; const downloadBtn = document.createElement('button'); downloadBtn.innerHTML = 'Download VSIX'; downloadBtn.style.cssText = 'margin-right: 5px; padding: 4px 8px; border: 1px solid #ccc; border-radius: 3px; background: #f5f5f5; cursor: pointer; font-size: 12px;'; downloadBtn.title = `下载 ${version} 版本`; downloadBtn.addEventListener('click', (e) => { e.preventDefault(); e.stopPropagation(); try { downloadVSIX(publisher, extensionName, version); } catch (error) { console.error('下载扩展时出错:', error); alert('下载失败,请查看控制台了解详情。'); } }); buttonsContainer.appendChild(downloadBtn); editors.forEach(editor => { const installBtn = document.createElement('button'); installBtn.innerHTML = `${editor.name}`; installBtn.style.cssText = 'margin-right: 5px; padding: 4px 8px; border: 1px solid #ccc; border-radius: 3px; background: #f0f8ff; cursor: pointer; font-size: 12px;'; installBtn.title = `安装到 ${editor.name}`; installBtn.addEventListener('click', (e) => { e.preventDefault(); e.stopPropagation(); try { const customUrl = `${editor.scheme}:extension/${publisher}.${extensionName}`; window.open(customUrl, '_self'); } catch (error) { console.error(`安装到 ${editor.name} 时出错:`, error); alert(`安装到 ${editor.name} 失败,请查看控制台了解详情。`); } }); buttonsContainer.appendChild(installBtn); }); row.appendChild(buttonsContainer); }); const headerRow = targetTable.querySelector('thead tr, tr:first-child'); if (headerRow && !headerRow.querySelector('.version-actions-header')) { const headerCell = document.createElement('th'); headerCell.className = 'version-actions-header'; headerCell.textContent = '操作'; headerCell.style.cssText = 'padding: 8px; text-align: center; font-weight: bold;'; headerRow.appendChild(headerCell); } } function initializeButtons() { addCustomInstallButtons(); setTimeout(() => { const customButtons = document.querySelector('.custom-install-buttons'); if (!customButtons) { addCustomInstallButtons(); } const versionButtons = document.querySelector('.version-custom-buttons'); if (!versionButtons) { const itemName = getItemNameFromURL(); if (itemName) { try { const { publisher, extensionName } = parseItemName(itemName); const editors = [ { name: 'Cursor', scheme: 'cursor', iconUrl: 'https://icon.horse/icon/cursor.sh' }, { name: 'Windsurf', scheme: 'windsurf', iconUrl: 'https://icon.horse/icon/windsurf.ai' }, { name: 'Trae', scheme: 'trae', iconUrl: 'https://lf-cdn.trae.ai/obj/trae-ai-sg/trae_website_prod/favicon.png' }, { name: 'Kiro', scheme: 'kiro', iconUrl: 'https://kiro.dev/favicon.ico' } ]; addVersionHistoryButtons(editors, publisher, extensionName); } catch (error) { console.error('无法为版本历史添加按钮:', error); } } } }, 2000); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initializeButtons); } else { initializeButtons(); } })();