Text Explainer Settings

Settings module for Text Explainer

目前为 2025-03-04 提交的版本。查看 最新版本

此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.gf.qytechs.cn/scripts/528763/1547386/Text%20Explainer%20Settings.js

// ==UserScript==
// @name         Text Explainer Settings
// @namespace    http://tampermonkey.net/
// @version      0.1.1
// @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</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</label>
        </div>
      </div>
      
      <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();
  }
}

// Make available globally and as a module if needed
window.TextExplainerSettings = TextExplainerSettings;

if (typeof module !== 'undefined') {
  module.exports = TextExplainerSettings;
}

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址