Download VSPackage from VSCode Marketplace

Adds a button to download the VSPackage from the VS Marketplace.

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Download VSPackage from VSCode Marketplace
// @name:zh-CN   从 VSCode 市场下载 VSPackage
// @namespace    http://tampermonkey.net/
// @version      0.5
// @description        Adds a button to download the VSPackage from the VS Marketplace.
// @description:zh-CN  添加一个按钮来从 VS Marketplace 下载 VSPackage。
// @author       9540536
// @match        https://marketplace.visualstudio.com/items?itemName=*
// @grant        none
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // --- 1. 配置与国际化 ---

    // 根据浏览器语言设置显示文本
    const isChinese = navigator.language.toLowerCase().startsWith('zh');
    const buttonText = isChinese ? '下载 VSPackage' : 'Download VSPackage';

    // 唯一的按钮ID,用于检查按钮是否存在
    const buttonId = 'vspackage-download-button-resilient';

    // --- 2. 从URL中解析关键信息 ---

    const urlParams = new URLSearchParams(window.location.search);
    const itemName = urlParams.get('itemName');

    // 如果URL中没有itemName参数,则脚本无法工作,直接停止
    if (!itemName) {
        console.error('VS Marketplace Downloader: Could not parse itemName from URL. Script stopped.');
        return;
    }
    const parts = itemName.split('.');
    const publisher = parts[0];
    const extensionName = parts.slice(1).join('.');

    // --- 3. 核心功能:检查并添加/修复按钮 (守护函数) ---

    // 使用XPath获取元素的辅助函数
    const getElementByXpath = (path) => {
        return document.evaluate(path, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
    };

    function ensureButtonExists() {
        // 如果按钮已存在,则什么都不做。这是此函数的关键,以避免不必要的DOM操作。
        if (document.getElementById(buttonId)) {
            return;
        }

        // XPath定位版本号和按钮应被添加的位置
        const versionXpath = '//*[@id="overviewTab"]/div/table/tbody/tr/td[2]/div[3]/div[5]/div/table/tbody/tr[1]/td[2]';
        const buttonLocationXpath = '//*[@id="section-banner"]/div/table/tbody/tr/td[2]/div/div[3]/div[1]/div/span[1]';

        const versionElement = getElementByXpath(versionXpath);
        const buttonLocationElement = getElementByXpath(buttonLocationXpath);

        // 只有当版本号和按钮位置都成功找到时,才继续
        if (versionElement && versionElement.textContent.trim() && buttonLocationElement) {
            const version = versionElement.textContent.trim();

            // 创建下载按钮
            const downloadButton = document.createElement('a');
            downloadButton.id = buttonId;
            downloadButton.textContent = buttonText;
            downloadButton.setAttribute('class', 'vscode-gallery-button'); // 沿用页面已有样式
            downloadButton.setAttribute('role', 'button');
            downloadButton.style.marginLeft = '10px';
            downloadButton.style.whiteSpace = 'nowrap'; // 防止按钮文本换行

            // 构建下载链接
            const targetUrl = `https://marketplace.visualstudio.com/_apis/public/gallery/publishers/${publisher}/vsextensions/${extensionName}/${version}/vspackage`;
            downloadButton.href = targetUrl;
            downloadButton.target = '_blank'; // 在新标签页打开

            // 将按钮添加到页面中
            buttonLocationElement.parentNode.appendChild(downloadButton);
        }
    }

    // --- 4. 启动持续监控 ---

    // 监控整个document.body以应对任何位置的DOM重新渲染
    const observer = new MutationObserver(ensureButtonExists);
    observer.observe(document.body, {
        childList: true, // 监控子节点的增加/删除
        subtree: true    // 监控所有后代节点的变化
    });

    // 初始运行时也执行一次,以应对页面初始加载就已完整的情况
    setTimeout(ensureButtonExists, 500);

})();