Greasy Fork

来自缓存

Greasy Fork is available in English.

随机密码生成器

在线随机密码生成器,可配置长度、字符集,一键复制

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         随机密码生成器
// @version      1.0
// @description  在线随机密码生成器,可配置长度、字符集,一键复制
// @author       You
// @match        *://*/*
// @grant        GM_setClipboard
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_registerMenuCommand
// @grant        GM_addStyle
// @namespace http://greasyfork.icu/users/1592203
// ==/UserScript==

(function () {
  'use strict';

  // ========== 默认配置 ==========
  const DEFAULT_CONFIG = {
    length: 16,
    uppercase: true,
    lowercase: true,
    numbers: true,
    symbols: true,
    excludeAmbiguous: false, // 排除易混淆字符
  };

  const CHARSETS = {
    uppercase: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
    lowercase: 'abcdefghijklmnopqrstuvwxyz',
    numbers: '0123456789',
    symbols: '!@#$%^&*()_+-=[]{}|;:,.<>?/~`',
  };

  const AMBIGUOUS_CHARS = 'Il1O0o';

  // ========== 加载/保存配置 ==========
  function loadConfig() {
    const saved = GM_getValue('pwdGenConfig', null);
    return saved ? { ...DEFAULT_CONFIG, ...JSON.parse(saved) } : { ...DEFAULT_CONFIG };
  }

  function saveConfig(config) {
    GM_setValue('pwdGenConfig', JSON.stringify(config));
  }

  // ========== 核心逻辑 ==========
  function buildCharset(config) {
    let chars = '';
    if (config.uppercase) chars += CHARSETS.uppercase;
    if (config.lowercase) chars += CHARSETS.lowercase;
    if (config.numbers) chars += CHARSETS.numbers;
    if (config.symbols) chars += CHARSETS.symbols;
    if (config.excludeAmbiguous) {
      chars = chars.split('').filter(c => !AMBIGUOUS_CHARS.includes(c)).join('');
    }
    return chars;
  }

  function generatePassword(config) {
    const charset = buildCharset(config);
    if (!charset) return '⚠ 请至少选择一种字符类型';

    const array = new Uint32Array(config.length);
    crypto.getRandomValues(array);

    let password = '';
    for (let i = 0; i < config.length; i++) {
      password += charset[array[i] % charset.length];
    }
    return password;
  }

  // 计算密码强度
  function getStrength(password) {
    let score = 0;
    if (password.length >= 8) score++;
    if (password.length >= 12) score++;
    if (password.length >= 16) score++;
    if (/[a-z]/.test(password)) score++;
    if (/[A-Z]/.test(password)) score++;
    if (/[0-9]/.test(password)) score++;
    if (/[^a-zA-Z0-9]/.test(password)) score++;
    return Math.min(score, 7);
  }

  function getStrengthLabel(score) {
    if (score <= 2) return { text: '弱', color: '#ef4444' };
    if (score <= 4) return { text: '中', color: '#f59e0b' };
    if (score <= 5) return { text: '强', color: '#22c55e' };
    return { text: '极强', color: '#10b981' };
  }

  // ========== UI 样式 ==========
  GM_addStyle(`
    #pwd-gen-toggle {
      position: fixed;
      bottom: 24px;
      right: 24px;
      width: 48px;
      height: 48px;
      border-radius: 50%;
      background: #6366f1;
      color: #fff;
      border: none;
      cursor: pointer;
      font-size: 22px;
      display: flex;
      align-items: center;
      justify-content: center;
      box-shadow: 0 4px 14px rgba(99,102,241,0.4);
      z-index: 2147483647;
      transition: transform 0.2s, box-shadow 0.2s;
      user-select: none;
    }
    #pwd-gen-toggle:hover {
      transform: scale(1.1);
      box-shadow: 0 6px 20px rgba(99,102,241,0.5);
    }

    #pwd-gen-panel {
      position: fixed;
      bottom: 84px;
      right: 24px;
      width: 340px;
      background: #1e1e2e;
      border-radius: 16px;
      padding: 24px;
      z-index: 2147483646;
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
      color: #cdd6f4;
      box-shadow: 0 8px 32px rgba(0,0,0,0.4);
      display: none;
      animation: pwdGenSlideUp 0.25s ease-out;
    }

    @keyframes pwdGenSlideUp {
      from { opacity: 0; transform: translateY(12px); }
      to { opacity: 1; transform: translateY(0); }
    }

    #pwd-gen-panel.visible { display: block; }

    .pg-title {
      font-size: 16px;
      font-weight: 700;
      color: #fff;
      margin: 0 0 18px 0;
      letter-spacing: 0.5px;
    }

    .pg-output-wrap {
      display: flex;
      gap: 8px;
      margin-bottom: 14px;
    }

    .pg-output {
      flex: 1;
      background: #313244;
      border: 1px solid #45475a;
      border-radius: 10px;
      padding: 12px 14px;
      font-family: 'Cascadia Code', 'Fira Code', 'JetBrains Mono', monospace;
      font-size: 15px;
      color: #a6e3a1;
      word-break: break-all;
      line-height: 1.5;
      min-height: 44px;
      user-select: all;
    }

    .pg-copy-btn {
      background: #6366f1;
      color: #fff;
      border: none;
      border-radius: 10px;
      padding: 0 16px;
      cursor: pointer;
      font-size: 13px;
      font-weight: 600;
      white-space: nowrap;
      transition: background 0.2s;
    }
    .pg-copy-btn:hover { background: #818cf8; }
    .pg-copy-btn.copied { background: #22c55e; }

    .pg-strength-bar {
      height: 4px;
      border-radius: 2px;
      background: #313244;
      margin-bottom: 18px;
      overflow: hidden;
    }
    .pg-strength-fill {
      height: 100%;
      border-radius: 2px;
      transition: width 0.3s, background 0.3s;
    }
    .pg-strength-label {
      font-size: 12px;
      margin-bottom: 6px;
      color: #a6adc8;
    }

    .pg-row {
      display: flex;
      align-items: center;
      justify-content: space-between;
      margin-bottom: 12px;
    }

    .pg-label {
      font-size: 13px;
      color: #a6adc8;
    }

    .pg-length-input {
      width: 64px;
      background: #313244;
      border: 1px solid #45475a;
      border-radius: 8px;
      padding: 6px 10px;
      color: #cdd6f4;
      font-size: 14px;
      text-align: center;
      outline: none;
    }
    .pg-length-input:focus { border-color: #6366f1; }

    .pg-slider {
      width: 140px;
      accent-color: #6366f1;
    }

    /* 自定义开关 */
    .pg-switch {
      position: relative;
      width: 40px;
      height: 22px;
      cursor: pointer;
    }
    .pg-switch input { display: none; }
    .pg-switch-track {
      position: absolute;
      inset: 0;
      background: #45475a;
      border-radius: 11px;
      transition: background 0.2s;
    }
    .pg-switch input:checked + .pg-switch-track { background: #6366f1; }
    .pg-switch-thumb {
      position: absolute;
      top: 2px;
      left: 2px;
      width: 18px;
      height: 18px;
      background: #fff;
      border-radius: 50%;
      transition: transform 0.2s;
      pointer-events: none;
    }
    .pg-switch input:checked ~ .pg-switch-thumb { transform: translateX(18px); }

    .pg-gen-btn {
      width: 100%;
      padding: 12px;
      margin-top: 6px;
      background: linear-gradient(135deg, #6366f1, #8b5cf6);
      color: #fff;
      border: none;
      border-radius: 10px;
      cursor: pointer;
      font-size: 14px;
      font-weight: 700;
      letter-spacing: 1px;
      transition: opacity 0.2s;
    }
    .pg-gen-btn:hover { opacity: 0.9; }
    .pg-gen-btn:active { transform: scale(0.98); }
  `);
  // ========== 构建 DOM ==========
  let config = loadConfig();
  let currentPassword = '';
  let hoverZoneActive = false;
  let hoverZoneTimer = null;

  // 悬浮按钮
  const toggle = document.createElement('button');
  toggle.id = 'pwd-gen-toggle';
  toggle.textContent = '🔑';
  toggle.title = '密码生成器 (Alt+P)';
  toggle.style.opacity = '0';
  toggle.style.pointerEvents = 'none';
  document.body.appendChild(toggle);

  // 鼠标靠近右下角时显示按钮
  const hoverZone = document.createElement('div');
  hoverZone.style.cssText = `
    position: fixed;
    bottom: 0;
    right: 0;
    width: 120px;
    height: 120px;
    z-index: 2147483645;
  `;
  document.body.appendChild(hoverZone);

  hoverZone.addEventListener('mouseenter', () => {
    clearTimeout(hoverZoneTimer);
    toggle.style.opacity = '1';
    toggle.style.pointerEvents = 'auto';
    hoverZoneActive = true;
  });

  // 鼠标离开按钮和角落区域后隐藏
  function scheduleHide() {
    hoverZoneTimer = setTimeout(() => {
      if (!panel.classList.contains('visible')) {
        toggle.style.opacity = '0';
        toggle.style.pointerEvents = 'none';
      }
      hoverZoneActive = false;
    }, 600);
  }

  hoverZone.addEventListener('mouseleave', scheduleHide);
  toggle.addEventListener('mouseleave', (e) => {
    // 如果鼠标移向面板,不隐藏
    if (!panel.contains(e.relatedTarget)) {
      scheduleHide();
    }
  });
  toggle.addEventListener('mouseenter', () => {
    clearTimeout(hoverZoneTimer);
  });

  // 面板关闭时也隐藏按钮
  const origToggleClick = () => {
    panel.classList.toggle('visible');
    if (panel.classList.contains('visible') && !currentPassword) {
      generate();
    }
    if (!panel.classList.contains('visible') && !hoverZoneActive) {
      toggle.style.opacity = '0';
      toggle.style.pointerEvents = 'none';
    }
  };

  // ========== 面板(与之前相同)==========
  const panel = document.createElement('div');
  panel.id = 'pwd-gen-panel';
  panel.innerHTML = `
    <div class="pg-title">🔐 随机密码生成器</div>

    <div class="pg-output-wrap">
      <div class="pg-output" id="pg-output"></div>
      <button class="pg-copy-btn" id="pg-copy">复制</button>
    </div>

    <div class="pg-strength-label" id="pg-strength-label">强度:-</div>
    <div class="pg-strength-bar"><div class="pg-strength-fill" id="pg-strength-fill"></div></div>

    <div class="pg-row">
      <span class="pg-label">密码长度</span>
      <input type="number" class="pg-length-input" id="pg-length" min="4" max="128" value="">
      <input type="range" class="pg-slider" id="pg-slider" min="4" max="128" value="">
    </div>

    <div class="pg-row">
      <span class="pg-label">大写字母 A-Z</span>
      <label class="pg-switch">
        <input type="checkbox" id="pg-upper" >
        <div class="pg-switch-track"></div>
        <div class="pg-switch-thumb"></div>
      </label>
    </div>

    <div class="pg-row">
      <span class="pg-label">小写字母 a-z</span>
      <label class="pg-switch">
        <input type="checkbox" id="pg-lower" >
        <div class="pg-switch-track"></div>
        <div class="pg-switch-thumb"></div>
      </label>
    </div>

    <div class="pg-row">
      <span class="pg-label">数字 0-9</span>
      <label class="pg-switch">
        <input type="checkbox" id="pg-numbers" >
        <div class="pg-switch-track"></div>
        <div class="pg-switch-thumb"></div>
      </label>
    </div>

    <div class="pg-row">
      <span class="pg-label">特殊符号</span>
      <label class="pg-switch">
        <input type="checkbox" id="pg-symbols" >
        <div class="pg-switch-track"></div>
        <div class="pg-switch-thumb"></div>
      </label>
    </div>

    <div class="pg-row">
      <span class="pg-label">排除易混淆字符</span>
      <label class="pg-switch">
        <input type="checkbox" id="pg-ambiguous" >
        <div class="pg-switch-track"></div>
        <div class="pg-switch-thumb"></div>
      </label>
    </div>

    <button class="pg-gen-btn" id="pg-generate">生成密码</button>
  `;
  document.body.appendChild(panel);

  // ========== 元素引用 ==========
  const outputEl = document.getElementById('pg-output');
  const copyBtn = document.getElementById('pg-copy');
  const lengthInput = document.getElementById('pg-length');
  const sliderInput = document.getElementById('pg-slider');
  const upperCb = document.getElementById('pg-upper');
  const lowerCb = document.getElementById('pg-lower');
  const numbersCb = document.getElementById('pg-numbers');
  const symbolsCb = document.getElementById('pg-symbols');
  const ambiguousCb = document.getElementById('pg-ambiguous');
  const genBtn = document.getElementById('pg-generate');
  const strengthFill = document.getElementById('pg-strength-fill');
  const strengthLabel = document.getElementById('pg-strength-label');

  // ========== 初始化配置 ==========
  function applyConfig(cfg) {
    lengthInput.value = cfg.length;
    sliderInput.value = cfg.length;
    upperCb.checked = cfg.uppercase;
    lowerCb.checked = cfg.lowercase;
    numbersCb.checked = cfg.numbers;
    symbolsCb.checked = cfg.symbols;
    ambiguousCb.checked = cfg.excludeAmbiguous;
  }
  applyConfig(config);

  // ========== 读取当前配置 ==========
  function readConfig() {
    config.length = Math.max(4, Math.min(128, parseInt(lengthInput.value) || 16));
    config.uppercase = upperCb.checked;
    config.lowercase = lowerCb.checked;
    config.numbers = numbersCb.checked;
    config.symbols = symbolsCb.checked;
    config.excludeAmbiguous = ambiguousCb.checked;
    return config;
  }

  // ========== 更新强度显示 ==========
  function updateStrength(password) {
    const score = getStrength(password);
    const { text, color } = getStrengthLabel(score);
    const percent = Math.round((score / 7) * 100);
    strengthFill.style.width = percent + '%';
    strengthFill.style.background = color;
    strengthLabel.textContent = `强度:${text}`;
    strengthLabel.style.color = color;
  }

  // ========== 生成并显示 ==========
  function generate() {
    const cfg = readConfig();
    currentPassword = generatePassword(cfg);
    outputEl.textContent = currentPassword;
    updateStrength(currentPassword);
    saveConfig(cfg);
  }

  // ========== 事件绑定 ==========
  toggle.addEventListener('click', origToggleClick);

  // 面板关闭按钮(点击面板外部关闭)
  document.addEventListener('click', (e) => {
    if (panel.classList.contains('visible') &&
        !panel.contains(e.target) &&
        !toggle.contains(e.target)) {
      panel.classList.remove('visible');
      if (!hoverZoneActive) {
        toggle.style.opacity = '0';
        toggle.style.pointerEvents = 'none';
      }
    }
  });

  // 快捷键 Alt+P
  document.addEventListener('keydown', (e) => {
    if (e.altKey && e.key.toLowerCase() === 'l') {
      e.preventDefault();
      toggle.style.opacity = '1';
      toggle.style.pointerEvents = 'auto';
      hoverZoneActive = true;
      origToggleClick();
    }
  });

  genBtn.addEventListener('click', generate);

  // 长度输入与滑块同步
  lengthInput.addEventListener('input', () => {
    sliderInput.value = lengthInput.value;
  });
  sliderInput.addEventListener('input', () => {
    lengthInput.value = sliderInput.value;
  });

  // 复制
  copyBtn.addEventListener('click', () => {
    if (!currentPassword) return;
    GM_setClipboard(currentPassword, 'text');
    copyBtn.textContent = '已复制 ✓';
    copyBtn.classList.add('copied');
    setTimeout(() => {
      copyBtn.textContent = '复制';
      copyBtn.classList.remove('copied');
    }, 1500);
  });

  // 油猴菜单命令
  GM_registerMenuCommand('🔐 打开密码生成器', () => {
    toggle.style.opacity = '1';
    toggle.style.pointerEvents = 'auto';
    hoverZoneActive = true;
    panel.classList.add('visible');
    if (!currentPassword) generate();
  });

})();