xiaozhuangzzz / 小庄智能管家

// ==UserScript==
// @name         小庄智能管家
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  一个强大的前端开发辅助工具,提供元素查看器、CSS选择器生成、API请求监控等功能,帮助开发者提高工作效率
// @author       小庄
// @match        *://*/*
// @icon         data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZmlsbD0iIzRjYWY1MCIgZD0iTTExLjksMTIuOTdjLTAuNDEtMC40MS0xLjA4LTAuNDEtMS40OSwwbC0yLjk4LDIuOThjLTAuNDEsMC40MS0wLjQxLDEuMDgsMCwxLjQ5YzAuNDEsMC40MSwxLjA4LDAuNDEsMS40OSwwTDExLjksMTQuNDVjMC40MS0wLjQxLDAuNDEtMS4wOCwwLTEuNDh6Ii8+PHBhdGggZmlsbD0iIzFiNWUyMCIgZD0iTTExLjE0LDcuMzdjNS44LDAuNDgsOS45OCw1LjUyLDkuNiwxMS4yMUMxMC44NiwxOC4yLDguMTcsMTUuNTIsNy43OCw5LjgzQzYuNzksMTIuMTksNi4xNiwxNC44Niw2LDE3LjYgYy00LjA0LTEuNzctNi4yNS02LjI3LTQuOTgtMTAuOThjMS4yOC00LjcxLDYuMTYtNy4xLDEwLjEyLTQuOThWNy4zN3oiLz48L3N2Zz4=
// @grant        GM_addStyle
// @grant        GM_registerMenuCommand
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        unsafeWindow
// @license      MIT
// ==/UserScript==

(function () {
  'use strict';

  // 样式定义
  const styles = `
        .dev-helper-panel {
            position: fixed;
            right: 0;
            top: 0;
            width: 300px;
            height: 100vh;
            background: #212121;
            color: #eee;
            z-index: 999999;
            font-family: Arial, sans-serif;
            font-size: 14px;
            box-shadow: -2px 0 5px rgba(0,0,0,0.3);
            transition: transform 0.3s;
            transform: translateX(300px);
            overflow: auto;
        }
        
        .dev-helper-panel.active {
            transform: translateX(0);
        }
        
        .dev-helper-toggle {
            position: fixed;
            top: 120px;
            right: 0;
            background: #4caf50;
            color: white;
            padding: 10px;
            cursor: pointer;
            z-index: 999999;
            border-radius: 5px 0 0 5px;
            box-shadow: -2px 0 5px rgba(0,0,0,0.2);
        }
        
        .dev-helper-header {
            padding: 10px;
            background: #4caf50;
            color: white;
            text-align: center;
        }
        
        .dev-helper-container {
            padding: 10px;
        }
        
        .dev-helper-tabs {
            display: flex;
            border-bottom: 1px solid #444;
        }
        
        .dev-helper-tab {
            padding: 8px 12px;
            cursor: pointer;
            border-bottom: 2px solid transparent;
        }
        
        .dev-helper-tab.active {
            border-bottom: 2px solid #4caf50;
            color: #4caf50;
        }
        
        .dev-helper-tab-content {
            padding: 10px 0;
            display: none;
        }
        
        .dev-helper-tab-content.active {
            display: block;
        }
        
        .dev-helper-button {
            background: #4caf50;
            border: none;
            color: white;
            padding: 5px 10px;
            margin: 5px 0;
            cursor: pointer;
            border-radius: 3px;
        }
        
        .dev-helper-table {
            width: 100%;
            border-collapse: collapse;
        }
        
        .dev-helper-table th, .dev-helper-table td {
            padding: 5px;
            border-bottom: 1px solid #444;
            text-align: left;
        }
        
        .dev-helper-label {
            display: block;
            margin: 5px 0;
        }
        
        .dev-helper-input {
            width: 100%;
            padding: 5px;
            margin-bottom: 10px;
            background: #333;
            color: #fff;
            border: 1px solid #555;
            border-radius: 3px;
        }
        
        .dev-helper-select {
            width: 100%;
            padding: 5px;
            margin-bottom: 10px;
            background: #333;
            color: #fff;
            border: 1px solid #555;
            border-radius: 3px;
        }
        
        .element-highlight {
            outline: 2px dashed #f44336 !important;
            background-color: rgba(244, 67, 54, 0.1) !important;
        }
        
        .request-item {
            margin-bottom: 8px;
            padding: 8px;
            background: #333;
            border-radius: 3px;
        }
        
        .request-method {
            display: inline-block;
            padding: 2px 5px;
            margin-right: 5px;
            border-radius: 3px;
        }
        
        .request-method.get {
            background: #3f51b5;
        }
        
        .request-method.post {
            background: #4caf50;
        }
        
        .request-method.put {
            background: #ff9800;
        }
        
        .request-method.delete {
            background: #f44336;
        }
        
        .request-url {
            word-break: break-all;
        }
        
        .collapsible-content {
            margin-top: 5px;
            display: none;
        }
        
        .collapsible-trigger {
            cursor: pointer;
            text-decoration: underline;
            color: #4caf50;
        }
        
        .color-preview {
            display: inline-block;
            width: 16px;
            height: 16px;
            margin-right: 5px;
            border: 1px solid #555;
            vertical-align: middle;
        }
    `;

  // 添加样式
  GM_addStyle(styles);

  // 创建UI
  function createUI() {
    const panel = document.createElement('div');
    panel.className = 'dev-helper-panel';
    panel.innerHTML = `
            <div class="dev-helper-header">
                <h3>小庄智能管家</h3>
            </div>
            <div class="dev-helper-container">
                <div class="dev-helper-tabs">
                    <div class="dev-helper-tab active" data-tab="element-inspector">元素查看器</div>
                    <div class="dev-helper-tab" data-tab="css-tools">CSS工具</div>
                    <div class="dev-helper-tab" data-tab="api-monitor">API监控</div>
                </div>
                
                <div class="dev-helper-tab-content active" data-tab="element-inspector">
                    <p>单击下方按钮,然后点击页面上的元素以查看其属性</p>
                    <button class="dev-helper-button" id="start-inspect">开始查看元素</button>
                    <div id="element-info">
                        <h4>元素信息</h4>
                        <div id="element-details"></div>
                    </div>
                </div>
                
                <div class="dev-helper-tab-content" data-tab="css-tools">
                    <div>
                        <h4>CSS选择器生成器</h4>
                        <button class="dev-helper-button" id="generate-selector">选择元素生成选择器</button>
                        <div id="selector-result"></div>
                    </div>
                    
                    <div style="margin-top: 20px;">
                        <h4>CSS颜色转换</h4>
                        <div>
                            <label class="dev-helper-label">输入颜色值</label>
                            <input type="text" class="dev-helper-input" id="color-input" placeholder="#FF0000 或 rgb(255,0,0)">
                            <button class="dev-helper-button" id="convert-color">转换</button>
                            <div id="color-result"></div>
                        </div>
                    </div>
                </div>
                
                <div class="dev-helper-tab-content" data-tab="api-monitor">
                    <h4>网络请求监控</h4>
                    <button class="dev-helper-button" id="clear-requests">清除请求</button>
                    <div id="requests-list"></div>
                </div>
            </div>
        `;

    document.body.appendChild(panel);

    // 创建切换按钮
    const toggle = document.createElement('div');
    toggle.className = 'dev-helper-toggle';
    toggle.textContent = '开发助手';
    toggle.addEventListener('click', function () {
      panel.classList.toggle('active');
      saveState();
    });

    document.body.appendChild(toggle);

    // 初始化标签页切换
    initializeTabs();

    // 初始化元素检查功能
    initializeElementInspector();

    // 初始化CSS工具
    initializeCssTools();

    // 初始化API监控
    initializeApiMonitor();

    // 加载已保存的状态
    loadState();
  }

  // 初始化标签页切换
  function initializeTabs() {
    const tabs = document.querySelectorAll('.dev-helper-tab');
    tabs.forEach(tab => {
      tab.addEventListener('click', function () {
        // 移除所有活动标签
        document.querySelectorAll('.dev-helper-tab').forEach(t => {
          t.classList.remove('active');
        });
        document.querySelectorAll('.dev-helper-tab-content').forEach(c => {
          c.classList.remove('active');
        });

        // 激活当前标签
        this.classList.add('active');
        const tabName = this.getAttribute('data-tab');
        document.querySelector(`.dev-helper-tab-content[data-tab="${tabName}"]`).classList.add('active');

        // 保存状态
        saveState();
      });
    });
  }

  // 初始化元素检查功能
  function initializeElementInspector() {
    let isInspecting = false;
    let highlightedElement = null;

    document.getElementById('start-inspect').addEventListener('click', function () {
      isInspecting = !isInspecting;
      this.textContent = isInspecting ? '停止查看元素' : '开始查看元素';

      if (!isInspecting && highlightedElement) {
        highlightedElement.classList.remove('element-highlight');
        highlightedElement = null;
      }
    });

    document.addEventListener('mouseover', function (e) {
      if (!isInspecting) return;

      if (highlightedElement) {
        highlightedElement.classList.remove('element-highlight');
      }

      if (!e.target.closest('.dev-helper-panel') && !e.target.closest('.dev-helper-toggle')) {
        highlightedElement = e.target;
        highlightedElement.classList.add('element-highlight');
      }
    }, true);

    document.addEventListener('click', function (e) {
      if (!isInspecting) return;

      if (!e.target.closest('.dev-helper-panel') && !e.target.closest('.dev-helper-toggle')) {
        e.preventDefault();
        e.stopPropagation();

        displayElementInfo(e.target);

        // 停止检查
        isInspecting = false;
        document.getElementById('start-inspect').textContent = '开始查看元素';
      }
    }, true);
  }

  // 显示元素信息
  function displayElementInfo(element) {
    const details = document.getElementById('element-details');

    // 获取元素标签名称
    const tagName = element.tagName.toLowerCase();

    // 获取元素ID
    const id = element.id ? element.id : '无';

    // 获取元素类名
    const className = element.className ? element.className : '无';

    // 获取元素样式
    const computedStyle = window.getComputedStyle(element);

    // 获取元素尺寸和位置
    const rect = element.getBoundingClientRect();

    // 生成唯一CSS选择器
    const selector = generateSelector(element);

    let html = `
            <table class="dev-helper-table">
                <tr>
                    <th>标签名</th>
                    <td>${tagName}</td>
                </tr>
                <tr>
                    <th>ID</th>
                    <td>${id}</td>
                </tr>
                <tr>
                    <th>类名</th>
                    <td>${className}</td>
                </tr>
                <tr>
                    <th>宽度</th>
                    <td>${Math.round(rect.width)}px</td>
                </tr>
                <tr>
                    <th>高度</th>
                    <td>${Math.round(rect.height)}px</td>
                </tr>
                <tr>
                    <th>位置</th>
                    <td>X: ${Math.round(rect.left)}px, Y: ${Math.round(rect.top)}px</td>
                </tr>
                <tr>
                    <th>CSS选择器</th>
                    <td>${selector}</td>
                </tr>
            </table>
            
            <h4 style="margin-top: 10px;">计算样式</h4>
            <div class="collapsible-trigger" data-target="computed-style">查看计算样式</div>
            <div class="collapsible-content" id="computed-style">
                <table class="dev-helper-table">
                    <tr>
                        <th>颜色</th>
                        <td><span class="color-preview" style="background-color: ${computedStyle.color}"></span>${computedStyle.color}</td>
                    </tr>
                    <tr>
                        <th>背景色</th>
                        <td><span class="color-preview" style="background-color: ${computedStyle.backgroundColor}"></span>${computedStyle.backgroundColor}</td>
                    </tr>
                    <tr>
                        <th>字体</th>
                        <td>${computedStyle.fontFamily}</td>
                    </tr>
                    <tr>
                        <th>字体大小</th>
                        <td>${computedStyle.fontSize}</td>
                    </tr>
                    <tr>
                        <th>行高</th>
                        <td>${computedStyle.lineHeight}</td>
                    </tr>
                    <tr>
                        <th>内边距</th>
                        <td>${computedStyle.padding}</td>
                    </tr>
                    <tr>
                        <th>外边距</th>
                        <td>${computedStyle.margin}</td>
                    </tr>
                    <tr>
                        <th>盒模型</th>
                        <td>${computedStyle.boxSizing}</td>
                    </tr>
                    <tr>
                        <th>显示方式</th>
                        <td>${computedStyle.display}</td>
                    </tr>
                    <tr>
                        <th>定位</th>
                        <td>${computedStyle.position}</td>
                    </tr>
                </table>
            </div>
            
            <h4 style="margin-top: 10px;">DOM属性</h4>
            <div class="collapsible-trigger" data-target="element-attributes">查看DOM属性</div>
            <div class="collapsible-content" id="element-attributes">
                <table class="dev-helper-table">
        `;

    // 添加元素的属性
    for (let i = 0; i < element.attributes.length; i++) {
      const attr = element.attributes[i];
      html += `
                <tr>
                    <th>${attr.name}</th>
                    <td>${attr.value}</td>
                </tr>
            `;
    }

    html += `
                </table>
            </div>
        `;

    details.innerHTML = html;

    // 初始化折叠面板
    initializeCollapsibles();
  }

  // 初始化折叠面板
  function initializeCollapsibles() {
    document.querySelectorAll('.collapsible-trigger').forEach(trigger => {
      trigger.addEventListener('click', function () {
        const targetId = this.getAttribute('data-target');
        const target = document.getElementById(targetId);

        if (target.style.display === 'block') {
          target.style.display = 'none';
          this.textContent = this.textContent.replace('隐藏', '查看');
        } else {
          target.style.display = 'block';
          this.textContent = this.textContent.replace('查看', '隐藏');
        }
      });
    });
  }

  // 生成CSS选择器
  function generateSelector(element) {
    let path = [];
    let current = element;

    while (current && current !== document && current !== document.documentElement) {
      // 尝试使用ID
      if (current.id) {
        path.unshift('#' + current.id);
        break; // ID应该是唯一的,所以我们可以在这里停止
      } else {
        // 使用标签名称和类(如果存在)
        let selector = current.tagName.toLowerCase();

        if (current.className) {
          const classes = current.className.split(' ').filter(c => c);
          if (classes.length > 0) {
            selector += '.' + classes.join('.');
          }
        }

        // 如果有相同类型的兄弟元素,添加索引
        let sibling = current;
        let index = 1;

        while (sibling = sibling.previousElementSibling) {
          if (sibling.tagName === current.tagName) {
            index++;
          }
        }

        if (index > 1) {
          selector += `:nth-of-type(${index})`;
        }

        path.unshift(selector);
        current = current.parentElement;
      }
    }

    return path.join(' > ');
  }

  // 初始化CSS工具
  function initializeCssTools() {
    let selectedElement = null;

    // CSS选择器生成
    document.getElementById('generate-selector').addEventListener('click', function () {
      this.textContent = '请选择一个元素...';

      const clickHandler = function (e) {
        if (!e.target.closest('.dev-helper-panel') && !e.target.closest('.dev-helper-toggle')) {
          e.preventDefault();
          e.stopPropagation();

          selectedElement = e.target;
          const selector = generateSelector(selectedElement);

          document.getElementById('selector-result').innerHTML = `
                        <div style="margin-top: 10px;">
                            <label class="dev-helper-label">生成的选择器:</label>
                            <input type="text" class="dev-helper-input" value="${selector}" readonly>
                            <button class="dev-helper-button" id="copy-selector">复制</button>
                        </div>
                    `;

          document.getElementById('copy-selector').addEventListener('click', function () {
            const selectorText = this.previousElementSibling.value;
            navigator.clipboard.writeText(selectorText).then(() => {
              this.textContent = '已复制!';
              setTimeout(() => {
                this.textContent = '复制';
              }, 2000);
            });
          });

          document.getElementById('generate-selector').textContent = '选择元素生成选择器';
          document.removeEventListener('click', clickHandler, true);
        }
      };

      document.addEventListener('click', clickHandler, true);
    });

    // 颜色转换
    document.getElementById('convert-color').addEventListener('click', function () {
      const colorInput = document.getElementById('color-input').value.trim();

      if (!colorInput) {
        document.getElementById('color-result').innerHTML = `<p style="color: #f44336;">请输入颜色值</p>`;
        return;
      }

      try {
        // 创建一个临时元素来计算颜色
        const tempElement = document.createElement('div');
        tempElement.style.color = colorInput;
        document.body.appendChild(tempElement);

        // 获取计算后的颜色
        const computedColor = window.getComputedStyle(tempElement).color;
        document.body.removeChild(tempElement);

        // 将RGB转换为HEX
        const rgbToHex = function (rgb) {
          if (!rgb) return '';

          // 检查是否已经是十六进制格式
          if (rgb.indexOf('#') === 0) {
            return rgb;
          }

          // 提取RGB值
          const rgbArr = rgb.match(/\d+/g);
          if (!rgbArr || rgbArr.length < 3) return '';

          const r = parseInt(rgbArr[0]);
          const g = parseInt(rgbArr[1]);
          const b = parseInt(rgbArr[2]);

          return '#' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1).toUpperCase();
        };

        // 将RGB转换为HSL
        const rgbToHsl = function (rgb) {
          if (!rgb) return '';

          // 提取RGB值
          const rgbArr = rgb.match(/\d+/g);
          if (!rgbArr || rgbArr.length < 3) return '';

          let r = parseInt(rgbArr[0]) / 255;
          let g = parseInt(rgbArr[1]) / 255;
          let b = parseInt(rgbArr[2]) / 255;

          const max = Math.max(r, g, b);
          const min = Math.min(r, g, b);
          let h, s, l = (max + min) / 2;

          if (max === min) {
            h = s = 0; // 灰色
          } else {
            const d = max - min;
            s = l > 0.5 ? d / (2 - max - min) : d / (max + min);

            switch (max) {
              case r: h = (g - b) / d + (g < b ? 6 : 0); break;
              case g: h = (b - r) / d + 2; break;
              case b: h = (r - g) / d + 4; break;
            }

            h = Math.round(h * 60);
          }

          s = Math.round(s * 100);
          l = Math.round(l * 100);

          return `hsl(${h}, ${s}%, ${l}%)`;
        };

        const hexColor = rgbToHex(computedColor);
        const hslColor = rgbToHsl(computedColor);

        document.getElementById('color-result').innerHTML = `
                    <div style="margin-top: 10px;">
                        <div>
                            <span class="color-preview" style="background-color: ${computedColor}"></span>
                            <span>预览</span>
                        </div>
                        <table class="dev-helper-table">
                            <tr>
                                <th>RGB</th>
                                <td>${computedColor}</td>
                            </tr>
                            <tr>
                                <th>HEX</th>
                                <td>${hexColor}</td>
                            </tr>
                            <tr>
                                <th>HSL</th>
                                <td>${hslColor}</td>
                            </tr>
                        </table>
                    </div>
                `;
      } catch (error) {
        document.getElementById('color-result').innerHTML = `<p style="color: #f44336;">无效的颜色值</p>`;
      }
    });
  }

  // 初始化API监控
  function initializeApiMonitor() {
    const requests = [];
    const maxRequests = 50;

    // 清除请求列表
    document.getElementById('clear-requests').addEventListener('click', function () {
      requests.length = 0;
      updateRequestsList();
    });

    // 拦截XMLHttpRequest
    const originalXHR = unsafeWindow.XMLHttpRequest;
    unsafeWindow.XMLHttpRequest = function () {
      const xhr = new originalXHR();
      const originalOpen = xhr.open;
      const originalSend = xhr.send;

      xhr.open = function () {
        this._method = arguments[0];
        this._url = arguments[1];
        return originalOpen.apply(this, arguments);
      };

      xhr.send = function () {
        this._startTime = new Date();
        this._data = arguments[0] || null;

        this.addEventListener('load', function () {
          try {
            const duration = new Date() - this._startTime;

            const requestInfo = {
              type: 'xhr',
              method: this._method,
              url: this._url,
              status: this.status,
              statusText: this.statusText,
              duration: duration,
              requestData: this._data,
              responseData: this.responseText,
              timestamp: new Date()
            };

            addRequest(requestInfo);
          } catch (e) {
            console.error('Error monitoring XHR:', e);
          }
        });

        return originalSend.apply(this, arguments);
      };

      return xhr;
    };

    // 拦截Fetch
    const originalFetch = unsafeWindow.fetch;
    unsafeWindow.fetch = function () {
      const startTime = new Date();
      const url = arguments[0] instanceof Request ? arguments[0].url : arguments[0];
      const options = arguments[1] || {};
      const method = options.method || 'GET';
      const data = options.body || null;

      return originalFetch.apply(this, arguments)
        .then(response => {
          const duration = new Date() - startTime;

          // 克隆响应以避免消耗流
          const clonedResponse = response.clone();

          clonedResponse.text().then(responseText => {
            const requestInfo = {
              type: 'fetch',
              method: method,
              url: url,
              status: response.status,
              statusText: response.statusText,
              duration: duration,
              requestData: data,
              responseData: responseText,
              timestamp: new Date()
            };

            addRequest(requestInfo);
          });

          return response;
        })
        .catch(error => {
          const duration = new Date() - startTime;

          const requestInfo = {
            type: 'fetch',
            method: method,
            url: url,
            status: 0,
            statusText: 'Error: ' + error.message,
            duration: duration,
            requestData: data,
            responseData: null,
            timestamp: new Date()
          };

          addRequest(requestInfo);

          throw error;
        });
    };

    // 添加请求到列表
    function addRequest(requestInfo) {
      requests.unshift(requestInfo);

      // 限制请求数量
      if (requests.length > maxRequests) {
        requests.pop();
      }

      updateRequestsList();
    }

    // 更新请求列表UI
    function updateRequestsList() {
      const requestsList = document.getElementById('requests-list');

      if (requests.length === 0) {
        requestsList.innerHTML = '<p>尚无请求数据</p>';
        return;
      }

      let html = '';

      requests.forEach((request, index) => {
        const statusClass = request.status >= 200 && request.status < 300 ? 'success' : 'error';
        const methodClass = request.method.toLowerCase();

        html += `
                    <div class="request-item">
                        <div>
                            <span class="request-method ${methodClass}">${request.method}</span>
                            <span class="request-status ${statusClass}">${request.status}</span>
                            <span>${Math.round(request.duration)}ms</span>
                        </div>
                        <div class="request-url">${request.url}</div>
                        <div class="collapsible-trigger" data-target="request-details-${index}">查看详情</div>
                        <div class="collapsible-content" id="request-details-${index}">
                            <table class="dev-helper-table">
                                <tr>
                                    <th>请求时间</th>
                                    <td>${request.timestamp.toLocaleTimeString()}</td>
                                </tr>
                                <tr>
                                    <th>请求类型</th>
                                    <td>${request.type}</td>
                                </tr>
                                <tr>
                                    <th>状态文本</th>
                                    <td>${request.statusText}</td>
                                </tr>
                            </table>
                            
                            <div style="margin-top: 10px;">
                                <div class="collapsible-trigger" data-target="request-data-${index}">查看请求数据</div>
                                <div class="collapsible-content" id="request-data-${index}">
                                    <pre style="max-height: 200px; overflow: auto; background: #333; padding: 5px; white-space: pre-wrap; word-break: break-all;">${formatData(request.requestData)}</pre>
                                </div>
                            </div>
                            
                            <div style="margin-top: 10px;">
                                <div class="collapsible-trigger" data-target="response-data-${index}">查看响应数据</div>
                                <div class="collapsible-content" id="response-data-${index}">
                                    <pre style="max-height: 200px; overflow: auto; background: #333; padding: 5px; white-space: pre-wrap; word-break: break-all;">${formatData(request.responseData)}</pre>
                                </div>
                            </div>
                        </div>
                    </div>
                `;
      });

      requestsList.innerHTML = html;

      // 初始化折叠面板
      initializeCollapsibles();
    }

    // 格式化数据
    function formatData(data) {
      if (!data) return '无数据';

      try {
        // 尝试解析并美化JSON
        const parsed = JSON.parse(data);
        return JSON.stringify(parsed, null, 2);
      } catch (e) {
        // 如果不是JSON,则直接返回原始数据
        return data;
      }
    }
  }

  // 保存面板状态
  function saveState() {
    const panel = document.querySelector('.dev-helper-panel');
    const isActive = panel.classList.contains('active');

    const activeTab = document.querySelector('.dev-helper-tab.active');
    const activeTabName = activeTab ? activeTab.getAttribute('data-tab') : 'element-inspector';

    GM_setValue('devHelperPanelState', {
      isActive,
      activeTab: activeTabName
    });
  }

  // 加载面板状态
  function loadState() {
    const savedState = GM_getValue('devHelperPanelState');

    if (savedState) {
      const panel = document.querySelector('.dev-helper-panel');

      if (savedState.isActive) {
        panel.classList.add('active');
      }

      if (savedState.activeTab) {
        // 激活保存的标签
        document.querySelectorAll('.dev-helper-tab').forEach(tab => {
          tab.classList.remove('active');
        });
        document.querySelectorAll('.dev-helper-tab-content').forEach(content => {
          content.classList.remove('active');
        });

        const tab = document.querySelector(`.dev-helper-tab[data-tab="${savedState.activeTab}"]`);
        const content = document.querySelector(`.dev-helper-tab-content[data-tab="${savedState.activeTab}"]`);

        if (tab && content) {
          tab.classList.add('active');
          content.classList.add('active');
        }
      }
    }
  }

  // 注册菜单命令
  GM_registerMenuCommand('显示/隐藏开发助手', function () {
    const panel = document.querySelector('.dev-helper-panel');
    panel.classList.toggle('active');
    saveState();
  });

  // 当页面加载完成后创建UI
  window.addEventListener('load', function () {
    // 检查页面上是否已有开发助手面板
    if (!document.querySelector('.dev-helper-panel')) {
      createUI();
    }
  });

  // 如果文档已经加载完成,则立即创建UI
  if (document.readyState === 'complete' || document.readyState === 'interactive') {
    createUI();
  }
})();