您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Settings module for Text Explainer
当前为
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.greasyfork.icu/scripts/528763/1547395/Text%20Explainer%20Settings.js
// ==UserScript== // @name Text Explainer Settings // @namespace http://tampermonkey.net/ // @version 0.1.2 // @description Settings module for Text Explainer // @author RoCry // @license MIT // ==/UserScript== class TextExplainerSettings { constructor(defaultConfig = {}) { this.defaultConfig = Object.assign({ model: "gemini-2.0-flash", apiKey: null, baseUrl: "https://generativelanguage.googleapis.com", provider: "gemini", language: "Chinese", shortcut: { key: "d", ctrlKey: false, altKey: true, shiftKey: false, metaKey: false }, floatingButton: { enabled: true, size: "medium", // small, medium, large position: "bottom-right" // top-left, top-right, bottom-left, bottom-right } }, defaultConfig); this.config = this.load(); } /** * Load settings from storage */ load() { try { const savedConfig = typeof GM_getValue === 'function' ? GM_getValue('explainerConfig', {}) : JSON.parse(localStorage.getItem('explainerConfig') || '{}'); return Object.assign({}, this.defaultConfig, savedConfig); } catch (e) { console.error('Error loading settings:', e); return Object.assign({}, this.defaultConfig); } } /** * Save settings to storage */ save() { try { if (typeof GM_setValue === 'function') { GM_setValue('explainerConfig', this.config); } else { localStorage.setItem('explainerConfig', JSON.stringify(this.config)); } return true; } catch (e) { console.error('Error saving settings:', e); return false; } } /** * Get setting value */ get(key) { return this.config[key]; } /** * Set setting value */ set(key, value) { this.config[key] = value; return this; } /** * Update multiple settings at once */ update(settings) { Object.assign(this.config, settings); return this; } /** * Reset settings to defaults */ reset() { this.config = Object.assign({}, this.defaultConfig); return this; } /** * Get all settings */ getAll() { return Object.assign({}, this.config); } /** * Open settings dialog */ openDialog(onSave = null) { // First check if dialog already exists and remove it const existingDialog = document.getElementById('explainer-settings-dialog'); if (existingDialog) existingDialog.remove(); // Create dialog container const dialog = document.createElement('div'); dialog.id = 'explainer-settings-dialog'; dialog.style = ` position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: white; padding: 16px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.2); z-index: 10001; width: 400px; max-width: 90vw; font-family: system-ui, sans-serif; `; // Add dark mode support const styleElement = document.createElement('style'); styleElement.textContent = ` #explainer-settings-dialog { color: #333; } #explainer-settings-dialog label { display: block; margin: 12px 0 4px; font-weight: 500; } #explainer-settings-dialog input[type="text"], #explainer-settings-dialog select { width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box; font-size: 14px; } #explainer-settings-dialog .buttons { display: flex; justify-content: flex-end; gap: 8px; margin-top: 20px; } #explainer-settings-dialog button { padding: 8px 12px; border: none; border-radius: 4px; cursor: pointer; font-size: 14px; } #explainer-settings-dialog button.primary { background-color: #4285f4; color: white; } #explainer-settings-dialog button.secondary { background-color: #f1f1f1; color: #333; } #explainer-settings-dialog .shortcut-section { display: flex; gap: 8px; } #explainer-settings-dialog .shortcut-section label { display: inline; margin-right: 4px; } #explainer-settings-dialog .shortcut-section input[type="checkbox"] { margin-right: 2px; } #explainer-settings-dialog .shortcut-key { width: 60px; } #explainer-settings-dialog .section-title { font-weight: 600; margin-top: 16px; border-bottom: 1px solid #ddd; padding-bottom: 4px; } @media (prefers-color-scheme: dark) { #explainer-settings-dialog { background: #333; color: #eee; } #explainer-settings-dialog input[type="text"], #explainer-settings-dialog select { background: #444; color: #eee; border-color: #555; } #explainer-settings-dialog button.secondary { background-color: #555; color: #eee; } #explainer-settings-dialog .section-title { border-bottom-color: #555; } } `; document.head.appendChild(styleElement); // Prepare shortcut configuration const shortcut = this.config.shortcut || this.defaultConfig.shortcut; const floatingButton = this.config.floatingButton || this.defaultConfig.floatingButton; // Create dialog content dialog.innerHTML = ` <h3 style="margin-top:0;">Text Explainer Settings</h3> <div class="section-title">Language & API Settings</div> <div> <label for="explainer-language">Language</label> <select id="explainer-language"> <option value="Chinese" ${this.config.language === 'Chinese' ? 'selected' : ''}>Chinese</option> <option value="English" ${this.config.language === 'English' ? 'selected' : ''}>English</option> <option value="Japanese" ${this.config.language === 'Japanese' ? 'selected' : ''}>Japanese</option> </select> </div> <div> <label for="explainer-provider">Provider</label> <select id="explainer-provider"> <option value="gemini" ${this.config.provider === 'gemini' ? 'selected' : ''}>Gemini</option> <option value="openai" ${this.config.provider === 'openai' ? 'selected' : ''}>OpenAI</option> <option value="anthropic" ${this.config.provider === 'anthropic' ? 'selected' : ''}>Anthropic</option> </select> </div> <div> <label for="explainer-model">Model</label> <input id="explainer-model" type="text" value="${this.config.model}"> </div> <div> <label for="explainer-api-key">API Key</label> <input id="explainer-api-key" type="text" value="${this.config.apiKey || ''}"> </div> <div> <label for="explainer-base-url">API Base URL</label> <input id="explainer-base-url" type="text" value="${this.config.baseUrl}"> </div> <div class="section-title">Shortcut Settings</div> <div class="shortcut-section"> <div> <label for="explainer-shortcut-key">Key</label> <input id="explainer-shortcut-key" class="shortcut-key" type="text" maxlength="1" value="${shortcut.key}"> </div> <div> <input type="checkbox" id="explainer-shortcut-ctrl" ${shortcut.ctrlKey ? 'checked' : ''}> <label for="explainer-shortcut-ctrl">Ctrl</label> </div> <div> <input type="checkbox" id="explainer-shortcut-alt" ${shortcut.altKey ? 'checked' : ''}> <label for="explainer-shortcut-alt">Alt/Option</label> </div> <div> <input type="checkbox" id="explainer-shortcut-shift" ${shortcut.shiftKey ? 'checked' : ''}> <label for="explainer-shortcut-shift">Shift</label> </div> <div> <input type="checkbox" id="explainer-shortcut-meta" ${shortcut.metaKey ? 'checked' : ''}> <label for="explainer-shortcut-meta">Meta/Cmd</label> </div> </div> <p class="shortcut-note" style="font-size: 12px; color: #666; margin-top: 5px;"> Note: Choose a letter key (a-z) for best cross-platform compatibility. At least one modifier key is recommended. </p> <div class="section-title">Touch Device Settings</div> <div> <input type="checkbox" id="explainer-floating-enabled" ${floatingButton.enabled ? 'checked' : ''}> <label for="explainer-floating-enabled">Show floating button on touch devices</label> </div> <div> <label for="explainer-floating-size">Button Size</label> <select id="explainer-floating-size"> <option value="small" ${floatingButton.size === 'small' ? 'selected' : ''}>Small</option> <option value="medium" ${floatingButton.size === 'medium' ? 'selected' : ''}>Medium</option> <option value="large" ${floatingButton.size === 'large' ? 'selected' : ''}>Large</option> </select> </div> <div> <label for="explainer-floating-position">Button Position</label> <select id="explainer-floating-position"> <option value="top-left" ${floatingButton.position === 'top-left' ? 'selected' : ''}>Top Left</option> <option value="top-right" ${floatingButton.position === 'top-right' ? 'selected' : ''}>Top Right</option> <option value="bottom-left" ${floatingButton.position === 'bottom-left' ? 'selected' : ''}>Bottom Left</option> <option value="bottom-right" ${floatingButton.position === 'bottom-right' ? 'selected' : ''}>Bottom Right</option> </select> </div> <div class="buttons"> <button id="explainer-settings-cancel" class="secondary">Cancel</button> <button id="explainer-settings-save" class="primary">Save</button> </div> `; document.body.appendChild(dialog); // Add event listeners document.getElementById('explainer-settings-save').addEventListener('click', () => { // Get shortcut settings const shortcutSettings = { key: document.getElementById('explainer-shortcut-key').value.toLowerCase(), ctrlKey: document.getElementById('explainer-shortcut-ctrl').checked, altKey: document.getElementById('explainer-shortcut-alt').checked, shiftKey: document.getElementById('explainer-shortcut-shift').checked, metaKey: document.getElementById('explainer-shortcut-meta').checked }; // Get floating button settings const floatingButtonSettings = { enabled: document.getElementById('explainer-floating-enabled').checked, size: document.getElementById('explainer-floating-size').value, position: document.getElementById('explainer-floating-position').value }; // Update config with all form values this.update({ language: document.getElementById('explainer-language').value, model: document.getElementById('explainer-model').value, apiKey: document.getElementById('explainer-api-key').value, baseUrl: document.getElementById('explainer-base-url').value, provider: document.getElementById('explainer-provider').value, shortcut: shortcutSettings, floatingButton: floatingButtonSettings }); // Save to storage this.save(); // Remove dialog dialog.remove(); styleElement.remove(); // Call save callback if provided if (typeof onSave === 'function') { onSave(this.config); } }); document.getElementById('explainer-settings-cancel').addEventListener('click', () => { dialog.remove(); styleElement.remove(); }); // Focus first field document.getElementById('explainer-language').focus(); // Add validation for the shortcut key const keyInput = document.getElementById('explainer-shortcut-key'); keyInput.addEventListener('input', () => { // Ensure it's a single character and convert to lowercase if (keyInput.value.length > 0) { keyInput.value = keyInput.value.charAt(0).toLowerCase(); } }); } } // Make available globally and as a module if needed window.TextExplainerSettings = TextExplainerSettings; if (typeof module !== 'undefined') { module.exports = TextExplainerSettings; }