Microsoft Store 連結產生器增強 (快捷標籤

為 store.rg-adguard.net 添加自定義快捷標籤,實現連結一鍵填充。採用現代 UI 界面並支持多語言。

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

You will need to install an extension such as Tampermonkey to install this script.

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name               Microsoft Store Generation Project Shortcut Customizer
// @name:ar            مخصص اختصارات مشروع توليد Microsoft Store
// @name:es            Personalizador de atajos de Microsoft Store Generation Project
// @name:fr            Personnalisateur de raccourcis Microsoft Store Generation Project
// @name:hi            Microsoft Store जनरेशन प्रोजेक्ट शॉर्टकट कस्टमाइज़र
// @name:id            Penyesuai Pintasan Proyek Generasi Microsoft Store
// @name:ja            Microsoft Store Generation Project ショートカット カスタマイザー
// @name:ko            Microsoft Store 생성 프로젝트 바로가기 사용자 정의
// @name:nl            Microsoft Store Generation Project Shortcut Customizer
// @name:pt-BR         Personalizador de atalhos do Microsoft Store Generation Project
// @name:ru            Настройщик ярлыков Microsoft Store Generation Project
// @name:vi            Trình tùy chỉnh phím tắt Microsoft Store Generation Project
// @name:zh-CN         Microsoft Store 链接生成器增强 (快捷标签)
// @name:zh-TW         Microsoft Store 連結產生器增強 (快捷標籤
// @version            1.3.2
// @description        Add custom shortcut tags to store.rg-adguard.net for quick link filling. Modern UI with i18n support.
// @description:ar     إضافة علامات اختصار مخصصة إلى store.rg-adguard.net لتعبئة الروابط بسرعة. واجهة حديثة مع دعم i18n.
// @description:es     Agregue etiquetas de acceso directo personalizadas a store.rg-adguard.net para completar enlaces rápidamente. Interfaz moderna con soporte i18n.
// @description:fr     Ajoutez des balises de raccourci personnalisées à store.rg-adguard.net pour un remplissage rapide des liens. Interface moderne avec support i18n.
// @description:hi     त्वरित लिंक भरने के लिए store.rg-adguard.net पर कस्टम शॉर्टकट टैग जोड़ें। i18n समर्थन के साथ आधुनिक यूआई।
// @description:id     Tambahkan tag pintasan kustom ke store.rg-adguard.net untuk pengisian tautan cepat. UI modern dengan dukungan i18n.
// @description:ja     リンクを素早く入力するために、store.rg-adguard.net にカスタム ショートカット タグを追加します。i18n サポートを備えたモダンな UI。
// @description:ko     빠른 링크 채우기를 위해 store.rg-adguard.net에 사용자 정의 바로가기 태그를 추가합니다. i18n 지원을 갖춘 최신 UI입니다.
// @description:nl     Voeg aangepaste sneltoetstags toe aan store.rg-adguard.net om links snel in te vullen. Moderne gebruikersinterface met i18n-ondersteuning.
// @description:pt-BR  Adicione tags de atalho personalizadas ao store.rg-adguard.net para preenchimento rápido de links. Interface moderna com suporte i18n.
// @description:ru     Добавьте настраиваемые теги ярлыков на store.rg-adguard.net для быстрого заполнения ссылок. Современный интерфейс с поддержкой i18n.
// @description:vi     Thêm thẻ ghi chú phím tắt tùy chỉnh vào store.rg-adguard.net để điền liên kết nhanh chóng. Giao diện hiện đại với hỗ trợ i18n.
// @description:zh-CN  为 store.rg-adguard.net 添加自定义快捷标签,实现链接一键填充。采用现代 UI 界面并支持多语言。
// @description:zh-TW  為 store.rg-adguard.net 添加自定義快捷標籤,實現連結一鍵填充。採用現代 UI 界面並支持多語言。
// @author             Femoon
// @match              https://store.rg-adguard.net/*
// @grant              none
// @license            MIT
// @noframes
// @namespace https://greasyfork.org/users/384092
// ==/UserScript==

(function() {
    'use strict';

    // UI 内部多语言配置
    const i18n = {
        en: {
            settingsBtn: "Settings",
            modalTitle: "Link Shortcuts",
            tableThName: "Name",
            tableThValue: "URL / Value",
            tableThOp: "Action",
            addRow: "+ Add Row",
            saveBtn: "Save Changes",
            placeholderName: "Name",
            placeholderValue: "URL",
            langSwitch: "中文",
            delBtn: "Remove"
        },
        zh: {
            settingsBtn: "配置快捷标签",
            modalTitle: "快捷链接管理",
            tableThName: "名称",
            tableThValue: "链接地址",
            tableThOp: "操作",
            addRow: "+ 新增行",
            saveBtn: "保存配置",
            placeholderName: "名称",
            placeholderValue: "URL地址",
            langSwitch: "English",
            delBtn: "删除"
        }
    };

    let currentLang = localStorage.getItem('tm_lang') || 'en';
    let configData = JSON.parse(localStorage.getItem('tm_store_config') || '[]');

    const t = (key) => i18n[currentLang][key];

    // CSS 样式:强化非衬线字体,微调阴影与缩放
    const style = document.createElement('style');
    style.innerHTML = `
        #tm-settings-trigger, #tm-modal, #tm-tag-container, .tm-modern-btn {
            font-family: "Segoe UI", "Inter", "Roboto", "Helvetica Neue", "Arial", -apple-system, sans-serif !important;
            -webkit-font-smoothing: antialiased;
        }

        :root {
            --primary-color: #0078d4;
            --btn-bg: rgba(255, 255, 255, 0.12);
            --panel-bg: rgba(255, 255, 255, 0.98);
            --subtle-shadow: 0 2px 5px rgba(0,0,0,0.1);
            --hover-shadow: 0 4px 8px rgba(0,0,0,0.15);
            --transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
        }

        .tm-modern-btn {
            background: var(--btn-bg);
            backdrop-filter: blur(10px);
            -webkit-backdrop-filter: blur(10px);
            border: 1px solid rgba(255, 255, 255, 0.25);
            color: white;
            padding: 7px 16px;
            border-radius: 8px;
            cursor: pointer;
            font-size: 13px;
            font-weight: 500;
            transition: var(--transition);
            box-shadow: var(--subtle-shadow);
            display: inline-flex;
            align-items: center;
            justify-content: center;
            letter-spacing: 0.1px;
        }

        .tm-modern-btn:hover {
            background: var(--primary-color);
            border-color: var(--primary-color);
            transform: translateY(-1px) scale(1.02);
            box-shadow: var(--hover-shadow);
        }

        .tm-modern-btn:active {
            transform: translateY(0) scale(0.98);
        }

        #tm-settings-trigger {
            position: fixed;
            top: 20px;
            right: 20px;
            z-index: 10000;
        }

        #tm-tag-container {
            display: flex;
            flex-wrap: wrap;
            gap: 10px;
            margin-bottom: 15px;
        }

        #tm-modal {
            display: none;
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            width: 480px;
            background: var(--panel-bg);
            border-radius: 16px;
            padding: 24px;
            z-index: 10001;
            box-shadow: 0 10px 30px rgba(0,0,0,0.2);
            color: #222;
        }

        #tm-modal-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 20px;
        }

        #tm-modal h3 { margin: 0; font-weight: 600; font-size: 17px; color: #111; }

        #tm-modal-overlay {
            display: none;
            position: fixed;
            top: 0; left: 0; width: 100%; height: 100%;
            background: rgba(0,0,0,0.3);
            z-index: 10000;
            backdrop-filter: blur(2px);
        }

        .tm-panel-btn {
            background: #f4f4f4;
            color: #444;
            border: 1px solid #ddd;
            box-shadow: none;
        }
        .tm-panel-btn:hover { background: #eee; border-color: #ccc; box-shadow: 0 1px 3px rgba(0,0,0,0.05); }

        .tm-save-btn { background: var(--primary-color); color: white; border: none; }

        .tm-input {
            width: 90%;
            padding: 6px 10px;
            border: 1px solid #ddd;
            border-radius: 6px;
            outline: none;
            font-family: inherit;
            font-size: 13px;
        }
        .tm-input:focus { border-color: var(--primary-color); }

        .tm-table { width: 100%; border-collapse: collapse; }
        .tm-table th { text-align: left; padding: 10px 5px; border-bottom: 1px solid #eee; font-size: 11px; color: #999; text-transform: uppercase; }
        .tm-table td { padding: 8px 0; border-bottom: 1px solid #f8f8f8; }
    `;
    document.head.appendChild(style);

    function initUI() {
        const settingsBtn = document.createElement('button');
        settingsBtn.id = 'tm-settings-trigger';
        settingsBtn.className = 'tm-modern-btn';
        document.body.appendChild(settingsBtn);

        const overlay = document.createElement('div');
        overlay.id = 'tm-modal-overlay';
        document.body.appendChild(overlay);

        const modal = document.createElement('div');
        modal.id = 'tm-modal';
        document.body.appendChild(modal);

        // 注入标签容器
        const mainInput = document.getElementById('url') || document.querySelector('input[type="text"]');
        if (mainInput) {
            const container = mainInput.closest('.input-group') || mainInput.parentElement;
            const tagBox = document.createElement('div');
            tagBox.id = 'tm-tag-container';
            container.parentNode.insertBefore(tagBox, container);
        }

        updateText();

        settingsBtn.onclick = () => { modal.style.display = 'block'; overlay.style.display = 'block'; renderTable(); };
        overlay.onclick = () => { modal.style.display = 'none'; overlay.style.display = 'none'; };
    }

    function updateText() {
        document.getElementById('tm-settings-trigger').innerText = t('settingsBtn');
        document.getElementById('tm-modal').innerHTML = `
            <div id="tm-modal-header">
                <h3>${t('modalTitle')}</h3>
                <button id="tm-lang-switch" class="tm-modern-btn tm-panel-btn" style="padding: 2px 8px; font-size: 11px;">${t('langSwitch')}</button>
            </div>
            <div style="max-height: 280px; overflow-y: auto;">
                <table class="tm-table">
                    <thead>
                        <tr><th>${t('tableThName')}</th><th>${t('tableThValue')}</th><th style="width:50px"></th></tr>
                    </thead>
                    <tbody id="tm-table-body"></tbody>
                </table>
            </div>
            <div style="margin-top: 20px; display: flex; justify-content: space-between;">
                <button id="tm-add-row" class="tm-modern-btn tm-panel-btn">${t('addRow')}</button>
                <button id="tm-save-config" class="tm-modern-btn tm-save-btn">${t('saveBtn')}</button>
            </div>
        `;

        document.getElementById('tm-lang-switch').onclick = () => {
            currentLang = currentLang === 'en' ? 'zh' : 'en';
            localStorage.setItem('tm_lang', currentLang);
            updateText();
            renderTable();
        };
        document.getElementById('tm-add-row').onclick = addRow;
        document.getElementById('tm-save-config').onclick = () => {
            saveConfig();
            document.getElementById('tm-modal').style.display = 'none';
            document.getElementById('tm-modal-overlay').style.display = 'none';
        };
        renderTags();
    }

    function renderTable() {
        const tbody = document.getElementById('tm-table-body');
        tbody.innerHTML = '';
        configData.forEach(item => {
            const row = document.createElement('tr');
            row.innerHTML = `
                <td><input class="tm-input name-input" value="${item.name}"></td>
                <td><input class="tm-input value-input" value="${item.value}"></td>
                <td><button class="tm-modern-btn tm-panel-btn" style="color:#d33; padding:4px 8px; font-size:11px;" onclick="this.parentElement.parentElement.remove()">✕</button></td>
            `;
            tbody.appendChild(row);
        });
        if(configData.length === 0) addRow();
    }

    function addRow() {
        const tbody = document.getElementById('tm-table-body');
        const row = document.createElement('tr');
        row.innerHTML = `
            <td><input class="tm-input name-input" placeholder="${t('placeholderName')}"></td>
            <td><input class="tm-input value-input" placeholder="${t('placeholderValue')}"></td>
            <td><button class="tm-modern-btn tm-panel-btn" style="color:#d33; padding:4px 8px; font-size:11px;" onclick="this.parentElement.parentElement.remove()">✕</button></td>
        `;
        tbody.appendChild(row);
    }

    function saveConfig() {
        const rows = document.querySelectorAll('#tm-table-body tr');
        const newData = [];
        rows.forEach(row => {
            const name = row.querySelector('.name-input').value.trim();
            const value = row.querySelector('.value-input').value.trim();
            if (name && value) newData.push({ name, value });
        });
        configData = newData;
        localStorage.setItem('tm_store_config', JSON.stringify(configData));
        renderTags();
    }

    function renderTags() {
        const container = document.getElementById('tm-tag-container');
        if (!container) return;
        container.innerHTML = '';
        configData.forEach(item => {
            const tag = document.createElement('button');
            tag.className = 'tm-modern-btn';
            tag.innerText = item.name;
            tag.onclick = () => {
                const mainInput = document.getElementById('url') || document.querySelector('input[type="text"]');
                if (mainInput) {
                    mainInput.value = item.value;
                    mainInput.dispatchEvent(new Event('input', { bubbles: true }));
                    mainInput.focus();
                }
            };
            container.appendChild(tag);
        });
    }

    if (document.readyState === 'complete') initUI();
    else window.addEventListener('load', initUI);

})();