Gimkit Enhanced Assistant

Enhanced learning assistant for Gimkit

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name        Gimkit Enhanced Assistant
// @namespace   Violentmonkey Scripts
// @match       *://*.gimkit.com/*
// @grant       none
// @version     1.0
// @author      CMH
// @description Enhanced learning assistant for Gimkit
// ==/UserScript==

(function() {
  'use strict';

  // Configuration
  const config = {
    highlightCorrectAnswers: true,
    autoAnswerDelay: 0, // Set to 0 to disable auto-answer
    showAnswerStats: true,
    enableKeyboardShortcuts: true
  };

  // Add custom CSS
  const style = document.createElement('style');
  style.textContent = `
    .enhanced-assistant-panel {
      position: fixed;
      top: 10px;
      right: 10px;
      background: rgba(30, 30, 30, 0.9);
      color: white;
      border: 2px solid #6c5ce7;
      border-radius: 8px;
      padding: 10px;
      z-index: 9999;
      max-width: 300px;
      box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
      font-family: Arial, sans-serif;
      transition: all 0.3s ease;
    }
    .enhanced-assistant-panel h3 {
      margin-top: 0;
      color: #6c5ce7;
      font-size: 16px;
      display: flex;
      justify-content: space-between;
      align-items: center;
    }
    .enhanced-assistant-button {
      background: #6c5ce7;
      color: white;
      border: none;
      padding: 5px 10px;
      border-radius: 4px;
      margin: 5px;
      cursor: pointer;
      transition: background 0.2s;
    }
    .enhanced-assistant-button:hover {
      background: #5b4bc4;
    }
    .enhanced-assistant-button.active {
      background: #4a3cb3;
      box-shadow: inset 0 0 5px rgba(0,0,0,0.3);
    }
    .study-notes {
      margin-top: 10px;
      border-top: 1px solid #444;
      padding-top: 10px;
    }
    .study-notes textarea {
      width: 100%;
      height: 100px;
      margin-top: 5px;
      border-radius: 4px;
      border: 1px solid #444;
      padding: 5px;
      background: #222;
      color: #eee;
    }
    .timer-display {
      font-size: 1.2em;
      font-weight: bold;
      margin: 5px 0;
      text-align: center;
    }
    .answer-stats {
      margin-top: 10px;
      border-top: 1px solid #444;
      padding-top: 10px;
    }
    .answer-stats-item {
      display: flex;
      justify-content: space-between;
      margin-bottom: 4px;
    }
    .answer-stats-item .question {
      flex: 1;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
      margin-right: 10px;
    }
    .answer-stats-item .answer {
      color: #6c5ce7;
      font-weight: bold;
    }
    .hidden {
      display: none;
    }
    .correct-answer {
      box-shadow: 0 0 0 2px #4CAF50 !important;
      position: relative;
    }
    .correct-answer::after {
      content: "✓";
      position: absolute;
      top: 5px;
      right: 5px;
      background: #4CAF50;
      color: white;
      width: 20px;
      height: 20px;
      border-radius: 50%;
      display: flex;
      align-items: center;
      justify-content: center;
      font-size: 12px;
    }
    .settings-section {
      margin-top: 10px;
      border-top: 1px solid #444;
      padding-top: 10px;
    }
    .settings-item {
      display: flex;
      justify-content: space-between;
      align-items: center;
      margin-bottom: 8px;
    }
    .toggle-switch {
      position: relative;
      display: inline-block;
      width: 40px;
      height: 20px;
    }
    .toggle-switch input {
      opacity: 0;
      width: 0;
      height: 0;
    }
    .toggle-slider {
      position: absolute;
      cursor: pointer;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      background-color: #444;
      transition: .4s;
      border-radius: 20px;
    }
    .toggle-slider:before {
      position: absolute;
      content: "";
      height: 16px;
      width: 16px;
      left: 2px;
      bottom: 2px;
      background-color: white;
      transition: .4s;
      border-radius: 50%;
    }
    input:checked + .toggle-slider {
      background-color: #6c5ce7;
    }
    input:checked + .toggle-slider:before {
      transform: translateX(20px);
    }
    .delay-input {
      width: 50px;
      background: #222;
      color: white;
      border: 1px solid #444;
      border-radius: 4px;
      padding: 2px 5px;
    }
    .keyboard-shortcuts {
      margin-top: 10px;
      border-top: 1px solid #444;
      padding-top: 10px;
      font-size: 12px;
    }
    .keyboard-shortcut-item {
      display: flex;
      justify-content: space-between;
      margin-bottom: 4px;
    }
    .keyboard-shortcut-item .key {
      background: #333;
      padding: 2px 6px;
      border-radius: 3px;
      border: 1px solid #555;
    }
    .made-by {
      font-size: 10px;
      opacity: 0.7;
      text-align: center;
      margin-top: 10px;
      border-top: 1px solid #444;
      padding-top: 5px;
    }
  `;
  document.head.appendChild(style);

  // Create the assistant panel
  const panel = document.createElement('div');
  panel.className = 'enhanced-assistant-panel';
  panel.innerHTML = `
    <h3>
      <span>Enhanced Assistant</span>
      <span class="version" style="font-size: 10px; opacity: 0.7;">v1.0</span>
    </h3>
    <div class="button-container">
      <button class="enhanced-assistant-button toggle-notes">Notes</button>
      <button class="enhanced-assistant-button toggle-timer">Timer</button>
      <button class="enhanced-assistant-button toggle-stats">Stats</button>
      <button class="enhanced-assistant-button toggle-settings">Settings</button>
      <button class="enhanced-assistant-button toggle-panel">Hide</button>
    </div>

    <div class="study-notes hidden">
      <p>Quick Notes:</p>
      <textarea placeholder="Take notes here..."></textarea>
      <button class="enhanced-assistant-button save-notes">Save</button>
    </div>

    <div class="timer-container hidden">
      <div class="timer-display">00:00</div>
      <div style="display: flex; justify-content: center;">
        <button class="enhanced-assistant-button start-timer">Start</button>
        <button class="enhanced-assistant-button reset-timer">Reset</button>
      </div>
    </div>

    <div class="answer-stats hidden">
      <p>Recent Answers:</p>
      <div class="answer-stats-list"></div>
    </div>

    <div class="settings-section hidden">
      <div class="settings-item">
        <span>Highlight Answers</span>
        <label class="toggle-switch">
          <input type="checkbox" id="highlight-toggle" ${config.highlightCorrectAnswers ? 'checked' : ''}>
          <span class="toggle-slider"></span>
        </label>
      </div>

      <div class="settings-item">
        <span>Auto-Answer Delay (ms)</span>
        <input type="number" id="auto-answer-delay" class="delay-input" value="${config.autoAnswerDelay}" min="0" max="10000" step="100">
      </div>

      <div class="settings-item">
        <span>Show Answer Stats</span>
        <label class="toggle-switch">
          <input type="checkbox" id="stats-toggle" ${config.showAnswerStats ? 'checked' : ''}>
          <span class="toggle-slider"></span>
        </label>
      </div>

      <div class="settings-item">
        <span>Keyboard Shortcuts</span>
        <label class="toggle-switch">
          <input type="checkbox" id="shortcuts-toggle" ${config.enableKeyboardShortcuts ? 'checked' : ''}>
          <span class="toggle-slider"></span>
        </label>
      </div>
    </div>

    <div class="keyboard-shortcuts ${config.enableKeyboardShortcuts ? '' : 'hidden'}">
      <p>Shortcuts:</p>
      <div class="keyboard-shortcut-item">
        <span>Toggle Panel</span>
        <span class="key">Alt+P</span>
      </div>
      <div class="keyboard-shortcut-item">
        <span>Quick Note</span>
        <span class="key">Alt+N</span>
      </div>
      <div class="keyboard-shortcut-item">
        <span>Timer Start/Pause</span>
        <span class="key">Alt+T</span>
      </div>
    </div>

    <div class="made-by">Made by CMH</div>
  `;
  document.body.appendChild(panel);

  // Timer functionality
  let timerInterval;
  let seconds = 0;
  const timerDisplay = panel.querySelector('.timer-display');
  const startTimerBtn = panel.querySelector('.start-timer');
  const resetTimerBtn = panel.querySelector('.reset-timer');

  function updateTimerDisplay() {
    const minutes = Math.floor(seconds / 60);
    const remainingSeconds = seconds % 60;
    timerDisplay.textContent = `${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}`;
  }

  startTimerBtn.addEventListener('click', function() {
    if (this.textContent === 'Start') {
      timerInterval = setInterval(function() {
        seconds++;
        updateTimerDisplay();
      }, 1000);
      this.textContent = 'Pause';
    } else {
      clearInterval(timerInterval);
      this.textContent = 'Start';
    }
  });

  resetTimerBtn.addEventListener('click', function() {
    clearInterval(timerInterval);
    seconds = 0;
    updateTimerDisplay();
    startTimerBtn.textContent = 'Start';
  });

  // Toggle functionality
  const toggleNotesBtn = panel.querySelector('.toggle-notes');
  const toggleTimerBtn = panel.querySelector('.toggle-timer');
  const toggleStatsBtn = panel.querySelector('.toggle-stats');
  const toggleSettingsBtn = panel.querySelector('.toggle-settings');
  const togglePanelBtn = panel.querySelector('.toggle-panel');

  const notesSection = panel.querySelector('.study-notes');
  const timerSection = panel.querySelector('.timer-container');
  const statsSection = panel.querySelector('.answer-stats');
  const settingsSection = panel.querySelector('.settings-section');

  function hideAllSections() {
    notesSection.classList.add('hidden');
    timerSection.classList.add('hidden');
    statsSection.classList.add('hidden');
    settingsSection.classList.add('hidden');

    toggleNotesBtn.classList.remove('active');
    toggleTimerBtn.classList.remove('active');
    toggleStatsBtn.classList.remove('active');
    toggleSettingsBtn.classList.remove('active');
  }

  toggleNotesBtn.addEventListener('click', function() {
    if (notesSection.classList.contains('hidden')) {
      hideAllSections();
      notesSection.classList.remove('hidden');
      this.classList.add('active');
    } else {
      notesSection.classList.add('hidden');
      this.classList.remove('active');
    }
  });

  toggleTimerBtn.addEventListener('click', function() {
    if (timerSection.classList.contains('hidden')) {
      hideAllSections();
      timerSection.classList.remove('hidden');
      this.classList.add('active');
    } else {
      timerSection.classList.add('hidden');
      this.classList.remove('active');
    }
  });

  toggleStatsBtn.addEventListener('click', function() {
    if (statsSection.classList.contains('hidden')) {
      hideAllSections();
      statsSection.classList.remove('hidden');
      this.classList.add('active');
    } else {
      statsSection.classList.add('hidden');
      this.classList.remove('active');
    }
  });

  toggleSettingsBtn.addEventListener('click', function() {
    if (settingsSection.classList.contains('hidden')) {
      hideAllSections();
      settingsSection.classList.remove('hidden');
      this.classList.add('active');
    } else {
      settingsSection.classList.add('hidden');
      this.classList.remove('active');
    }
  });

  togglePanelBtn.addEventListener('click', function() {
    if (this.textContent === 'Hide') {
      hideAllSections();
      panel.style.width = 'auto';
      panel.style.height = 'auto';
      panel.style.overflow = 'hidden';
      panel.style.padding = '5px';
      Array.from(panel.children).forEach(child => {
        if (child.tagName !== 'H3' && !child.contains(togglePanelBtn)) {
          child.style.display = 'none';
        }
      });
      this.textContent = 'Show';
    } else {
      panel.style.width = '';
      panel.style.height = '';
      panel.style.overflow = '';
      panel.style.padding = '10px';
      Array.from(panel.children).forEach(child => {
        if (child.tagName !== 'H3') {
          child.style.display = '';
        }
      });
      this.textContent = 'Hide';
    }
  });

  // Save notes functionality
  const saveNotesBtn = panel.querySelector('.save-notes');
  const notesTextarea = panel.querySelector('textarea');

  saveNotesBtn.addEventListener('click', function() {
    const notes = notesTextarea.value;
    localStorage.setItem('gimkitEnhancedNotes', notes);
    alert('Notes saved!');
  });

  // Load saved notes
  const savedNotes = localStorage.getItem('gimkitEnhancedNotes');
  if (savedNotes) {
    notesTextarea.value = savedNotes;
  }

  // Settings functionality
  const highlightToggle = document.getElementById('highlight-toggle');
  const autoAnswerDelayInput = document.getElementById('auto-answer-delay');
  const statsToggle = document.getElementById('stats-toggle');
  const shortcutsToggle = document.getElementById('shortcuts-toggle');
  const keyboardShortcutsSection = panel.querySelector('.keyboard-shortcuts');

  highlightToggle.addEventListener('change', function() {
    config.highlightCorrectAnswers = this.checked;
    localStorage.setItem('gimkitConfig', JSON.stringify(config));
  });

  autoAnswerDelayInput.addEventListener('change', function() {
    config.autoAnswerDelay = parseInt(this.value, 10);
    localStorage.setItem('gimkitConfig', JSON.stringify(config));
  });

  statsToggle.addEventListener('change', function() {
    config.showAnswerStats = this.checked;
    localStorage.setItem('gimkitConfig', JSON.stringify(config));
  });

  shortcutsToggle.addEventListener('change', function() {
    config.enableKeyboardShortcuts = this.checked;
    localStorage.setItem('gimkitConfig', JSON.stringify(config));
    if (this.checked) {
      keyboardShortcutsSection.classList.remove('hidden');
    } else {
      keyboardShortcutsSection.classList.add('hidden');
    }
  });

  // Load saved config
  const savedConfig = localStorage.getItem('gimkitConfig');
  if (savedConfig) {
    try {
      const parsedConfig = JSON.parse(savedConfig);
      Object.assign(config, parsedConfig);

      // Update UI to match loaded config
      highlightToggle.checked = config.highlightCorrectAnswers;
      autoAnswerDelayInput.value = config.autoAnswerDelay;
      statsToggle.checked = config.showAnswerStats;
      shortcutsToggle.checked = config.enableKeyboardShortcuts;

      if (config.enableKeyboardShortcuts) {
        keyboardShortcutsSection.classList.remove('hidden');
      } else {
        keyboardShortcutsSection.classList.add('hidden');
      }
    } catch (e) {
      console.error('Error loading saved config:', e);
    }
  }

  // Answer tracking
  const answerMap = new Map();
  const answerStatsList = panel.querySelector('.answer-stats-list');

  function updateAnswerStats() {
    if (!config.showAnswerStats) return;

    answerStatsList.innerHTML = '';

    // Get the last 5 entries
    const entries = Array.from(answerMap.entries()).slice(-5);

    entries.forEach(([question, answer]) => {
      const item = document.createElement('div');
      item.className = 'answer-stats-item';
      item.innerHTML = `
        <span class="question" title="${question}">${question}</span>
        <span class="answer">${answer}</span>
      `;
      answerStatsList.appendChild(item);
    });
  }

  // Keyboard shortcuts
  if (config.enableKeyboardShortcuts) {
    document.addEventListener('keydown', function(e) {
      // Alt+P to toggle panel
      if (e.altKey && e.key === 'p') {
        togglePanelBtn.click();
      }

      // Alt+N to toggle notes
      if (e.altKey && e.key === 'n') {
        toggleNotesBtn.click();
      }

      // Alt+T to toggle timer
      if (e.altKey && e.key === 't') {
        toggleTimerBtn.click();
      }
    });
  }

  // Detect quiz questions and provide assistance
  const observer = new MutationObserver(function(mutations) {
    mutations.forEach(function(mutation) {
      if (mutation.addedNodes && mutation.addedNodes.length > 0) {
        for (let i = 0; i < mutation.addedNodes.length; i++) {
          const node = mutation.addedNodes[i];
          if (node.nodeType === 1) {
            // Check if this is a question container
            const questionElement = node.querySelector && node.querySelector('[data-testid="question-text"]');
            if (questionElement) {
              const questionText = questionElement.textContent.trim();

              // Look for answer options
              const answerOptions = Array.from(document.querySelectorAll('[role="button"]')).filter(el =>
                el.textContent && el.textContent.length > 0 && !el.textContent.includes('Skip')
              );

              // If we have a stored answer for this question
              if (answerMap.has(questionText) && config.highlightCorrectAnswers) {
                const correctAnswer = answerMap.get(questionText);

                // Find and highlight the correct answer
                answerOptions.forEach(option => {
                  if (option.textContent.trim() === correctAnswer) {
                    option.classList.add('correct-answer');

                    // Auto-answer if enabled
                    if (config.autoAnswerDelay > 0) {
                      setTimeout(() => {
                        option.click();
                      }, config.autoAnswerDelay);
                    }
                  }
                });
              }

              // Add click listeners to capture correct answers
              answerOptions.forEach(option => {
                option.addEventListener('click', function() {
                  // We'll check after a short delay if the answer was correct
                  setTimeout(() => {
                    // If we're still on the same question, the answer was wrong
                    // If we moved to a new question, the answer was correct
                    const currentQuestion = document.querySelector('[data-testid="question-text"]');
                    if (currentQuestion && currentQuestion.textContent.trim() !== questionText) {
                      // The answer was correct, store it
                      answerMap.set(questionText, option.textContent.trim());
                      updateAnswerStats();
                    }
                  }, 500);
                }, { once: true });
              });
            }
          }
        }
      }
    });
  });

  observer.observe(document.body, {
    childList: true,
    subtree: true
  });

  console.log('Gimkit Enhanced Assistant loaded successfully!');
})();