TornPDA RR Tracker v5.0 (All Features)

Sure, here is a simple and customer-friendly description on how to use the tracker:

目前為 2025-07-10 提交的版本,檢視 最新版本

// ==UserScript==
// @name         TornPDA RR Tracker v5.0 (All Features)
// @namespace    https://greasyfork.org/users/1493252
// @version      5.0 // Increased version for all integrated features
// @description  Sure, here is a simple and customer-friendly description on how to use the tracker:
//###Your Torn RR Tracker: Quick Guide
//###Auto-Tracking
//###Just open Russian Roulette, and your tracker starts automatically. It records every win and loss, showing your profit, win rate, and streaks.
//###Panel Controls
//###* Drag: Click and drag the ☰ icon to move the panel anywhere you like.
//###* Collapse/Expand: Click the ► (or ▪) icon to shrink or expand the panel.
//###* Hide/Show: If Auto-Hide is on in settings, the tracker hides when you leave RR and reappears when you return.
//###Settings (⚙️ icon)
//###* Panel Opacity: Adjust how see-through the panel is.
//###* Profit/Loss Alerts: Set targets to be notified when you hit a certain profit or loss.
//###* Mini-Bar Count: Choose how many recent games show in the collapsed view.
//###* Reset Data: Clear all your tracked stats and profit.

// @match        https://www.torn.com/page.php?sid=russianRoulette*
// @grant        none
// @run-at       document-idle
// @license      MIT
// ==/UserScript==

(function waitUntilReady() {
  const PANEL_ID      = 'rr-tracker-panel';
  const STORAGE       = 'torn_rr_tracker_results';
  const PROFIT_STORAGE = 'torn_rr_total_profit';
  const POS_KEY       = 'rr_panelPos';
  const COLLAPSE_KEY  = 'rr_panelCollapsed';
  const AUTOHIDE_KEY  = 'rr_autoHide';
  const MAX_DISPLAY_KEY = 'rr_maxDisplayMatches';

  // NEW: Opacity Storage Key
  const OPACITY_KEY   = 'rr_panelOpacity';
  // NEW: Alert Threshold Storage Keys
  const PROFIT_TARGET_KEY      = 'rr_profitTarget';
  const LOSS_LIMIT_KEY         = 'rr_lossLimit';
  const ALERT_SHOWN_PROFIT_KEY = 'rr_alertShownProfit';
  const ALERT_SHOWN_LOSS_KEY   = 'rr_alertShownLoss';
  // NEW: Mini-Bar Count Storage Key
  const MINI_BAR_COUNT_KEY = 'rr_miniBarCount';


  if (document.getElementById(PANEL_ID)) return;

  if (!document.body.innerText.includes('Password') &&
      !document.body.innerText.includes('POT MONEY')) {
    return setTimeout(waitUntilReady, 200);
  }

  let lastPot      = 0;
  let roundActive  = true;
  let hasTracked   = false;
  let results      = JSON.parse(localStorage.getItem(STORAGE) || '[]');
  let totalProfit  = parseFloat(localStorage.getItem(PROFIT_STORAGE) || '0');
  let collapsed    = JSON.parse(localStorage.getItem(COLLAPSE_KEY) || 'false');
  let autoHide     = JSON.parse(localStorage.getItem(AUTOHIDE_KEY) || 'true');
  let showSettings = false;

  // Initialize maxDisplayMatches from localStorage, default to 100
  let maxDisplayMatches = parseInt(localStorage.getItem(MAX_DISPLAY_KEY) || '100', 10);
  if (isNaN(maxDisplayMatches) || maxDisplayMatches < 1) {
      maxDisplayMatches = 100;
      localStorage.setItem(MAX_DISPLAY_KEY, maxDisplayMatches.toString());
  }

  // Initialize currentOpacity from localStorage, default to 0.6
  let currentOpacity  = parseFloat(localStorage.getItem(OPACITY_KEY) || '0.6');
  if (isNaN(currentOpacity) || currentOpacity < 0.1 || currentOpacity > 1.0) {
      currentOpacity = 0.6; // Sanitize if value is bad
      localStorage.setItem(OPACITY_KEY, currentOpacity.toString());
  }

  // Initialize Alert Thresholds and Status
  let profitTarget = parseFloat(localStorage.getItem(PROFIT_TARGET_KEY) || '0'); // 0 means disabled
  let lossLimit    = parseFloat(localStorage.getItem(LOSS_LIMIT_KEY) || '0');   // 0 means disabled
  let alertShownProfit = JSON.parse(localStorage.getItem(ALERT_SHOWN_PROFIT_KEY) || 'false');
  let alertShownLoss   = JSON.parse(localStorage.getItem(ALERT_SHOWN_LOSS_KEY) || 'false');

  // NEW: Initialize miniBarCount from localStorage, default to 10
  let miniBarCount = parseInt(localStorage.getItem(MINI_BAR_COUNT_KEY) || '10', 10);
  if (isNaN(miniBarCount) || miniBarCount < 1 || miniBarCount > 50) { // Max 50 for sanity
      miniBarCount = 10;
      localStorage.setItem(MINI_BAR_COUNT_KEY, miniBarCount.toString());
  }


  const panel = document.createElement('div');
  panel.id = PANEL_ID;
  Object.assign(panel.style, {
    position:      'fixed',
    top:           '12px',
    left:          '12px',
    background:    `rgba(0,0,0,${currentOpacity})`, // Uses dynamic opacity
    color:         '#fff',
    fontFamily:    'monospace',
    fontSize:      '14px',
    padding:       '36px 12px 12px',
    borderRadius:  '10px',
    boxShadow:     '0 0 12px rgba(255,0,0,0.3)',
    zIndex:        '9999999',
    userSelect:    'none',
    display:       'flex',
    flexDirection: 'column',
    gap:           '8px',
    minWidth:      '140px'
  });
  document.body.appendChild(panel);

  try {
    const pos = JSON.parse(localStorage.getItem(POS_KEY) || '{}');
    if (pos.top && pos.left) {
      panel.style.top  = pos.top;
      panel.style.left = pos.left;
    }
  } catch {}

  // NEW: Alert Message Div - Placed before miniBar and statsGroup
  const alertMessageDiv = document.createElement('div');
  alertMessageDiv.id = 'rr-alert-message';
  Object.assign(alertMessageDiv.style, {
      display:         'none', // Hidden by default
      padding:         '8px',
      marginBottom:    '8px',
      borderRadius:    '6px',
      textAlign:       'center',
      fontWeight:      'bold',
      fontSize:        '16px',
      color:           'white',
      cursor:          'pointer', // Make it clickable to dismiss
      border:          '1px solid transparent', // Will be colored later
      transition:      'background-color 0.3s, border-color 0.3s'
  });
  panel.appendChild(alertMessageDiv);


  const miniBar = document.createElement('div');
  Object.assign(miniBar.style, {
    display:  'none',
    flexWrap: 'wrap',
    gap:      '2px',
    padding:  '4px 0'
  });
  panel.appendChild(miniBar);

  const profitMini = document.createElement('div');
  Object.assign(profitMini.style, {
    display:    'none',
    fontSize:   '14px',
    fontFamily: 'monospace',
    margin:     '2px 0'
  });
  panel.appendChild(profitMini);

  const statusDiv = document.createElement('div');
  Object.assign(statusDiv.style, {
    position: 'absolute', top: '8px', left: '8px',
    width:    '20px',    height: '20px',
    fontSize: '18px',    cursor: 'pointer',
    color:    'rgba(255,255,255,0.7)'
  });
  panel.appendChild(statusDiv);

  const dragHandle = document.createElement('div');
  dragHandle.textContent = '☰';
  Object.assign(dragHandle.style, {
    position:    'absolute',
    top:         '8px',
    right:       '8px',
    width:       '20px',
    height:      '20px',
    fontSize:    '18px',
    cursor:      'move',
    color:       'rgba(255,255,255,0.7)',
    touchAction: 'none'
  });
  panel.appendChild(dragHandle);

  // --- Main Stats Group ---
  const statsGroup = document.createElement('div');
  Object.assign(statsGroup.style, {
    display:       'flex',
    flexDirection: 'column',
    gap:           '4px'
  });
  panel.appendChild(statsGroup);

  const profitDiv  = document.createElement('div');
  const winrateDiv = document.createElement('div');
  const streakDiv  = document.createElement('div');
  statsGroup.append(profitDiv, winrateDiv, streakDiv);

  const resultsContainer = document.createElement('div');
  Object.assign(resultsContainer.style, {
    maxHeight: '140px',
    overflowY: 'auto',
    marginTop: '4px'
  });
  statsGroup.appendChild(resultsContainer);

  // --- Settings Panel ---
  const settingsPanel = document.createElement('div');
  Object.assign(settingsPanel.style, {
    display:       'none',
    flexDirection: 'column',
    gap:           '8px',
    padding:       '12px 0'
  });
  panel.appendChild(settingsPanel);

  const settingsTitle = document.createElement('div');
  settingsTitle.textContent = 'Settings';
  Object.assign(settingsTitle.style, {
      fontSize: '16px',
      fontWeight: 'bold',
      marginBottom: '4px'
  });
  settingsPanel.appendChild(settingsTitle);

  const backButton = document.createElement('button');
  backButton.textContent = '← Back';
  Object.assign(backButton.style, {
    alignSelf:   'flex-start',
    background:  'rgba(255,255,255,0.1)',
    color:       '#fff',
    border:      'none',
    borderRadius:'6px',
    padding:     '4px 8px',
    cursor:      'pointer',
    marginBottom: '8px'
  });
  backButton.onmouseenter = () => backButton.style.background = 'rgba(255,255,255,0.2)';
  backButton.onmouseleave = () => backButton.style.background = 'rgba(255,255,255,0.1)';
  backButton.onclick = () => {
      showSettings = false;
      refreshAll();
  };
  settingsPanel.appendChild(backButton);

  // Max Matches Displayed Setting
  const maxMatchesSettingDiv = document.createElement('div');
  Object.assign(maxMatchesSettingDiv.style, {
      display: 'flex',
      alignItems: 'center',
      gap: '8px',
      marginBottom: '8px'
  });
  const maxMatchesLabel = document.createElement('label');
  maxMatchesLabel.textContent = 'Max Matches Displayed:';
  maxMatchesLabel.htmlFor = 'max-matches-input';
  Object.assign(maxMatchesLabel.style, { flexShrink: '0' });

  const maxMatchesInput = document.createElement('input');
  maxMatchesInput.type = 'number';
  maxMatchesInput.id = 'max-matches-input';
  maxMatchesInput.min = '1'; // Minimum 1 match
  maxMatchesInput.max = '500'; // Reasonable upper limit
  maxMatchesInput.value = maxDisplayMatches;
  Object.assign(maxMatchesInput.style, {
      width: '60px',
      padding: '4px',
      border: '1px solid #555',
      borderRadius: '4px',
      background: 'rgba(255,255,255,0.1)',
      color: '#fff'
  });
  maxMatchesInput.onchange = () => {
      let newValue = parseInt(maxMatchesInput.value, 10);
      if (isNaN(newValue) || newValue < 1) {
          newValue = 1; // Default to 1 if invalid
      }
      if (newValue > 500) newValue = 500; // Cap at 500
      maxDisplayMatches = newValue;
      maxMatchesInput.value = maxDisplayMatches; // Update input field to sanitized value
      localStorage.setItem(MAX_DISPLAY_KEY, maxDisplayMatches.toString());
      // If we reduce the max matches, also trim the results array immediately
      while (results.length > maxDisplayMatches) {
          results.pop();
      }
      saveResults();
      refreshAll();
  };
  maxMatchesSettingDiv.append(maxMatchesLabel, maxMatchesInput);
  settingsPanel.appendChild(maxMatchesSettingDiv);

  // FEATURE: Panel Opacity Slider Setting
  const transparencySettingDiv = document.createElement('div');
  Object.assign(transparencySettingDiv.style, {
      display: 'flex',
      alignItems: 'center',
      gap: '8px',
      marginBottom: '8px'
  });
  const transparencyLabel = document.createElement('label');
  transparencyLabel.textContent = 'Panel Opacity:';
  transparencyLabel.htmlFor = 'transparency-slider';
  Object.assign(transparencyLabel.style, { flexShrink: '0' });

  const transparencySlider = document.createElement('input');
  transparencySlider.type = 'range';
  transparencySlider.id = 'transparency-slider';
  transparencySlider.min = '0.1'; // Minimum opacity (10%)
  transparencySlider.max = '1.0'; // Maximum opacity (100%)
  transparencySlider.step = '0.05'; // Increment/decrement by 5%
  transparencySlider.value = currentOpacity; // Set initial value from the loaded variable
  Object.assign(transparencySlider.style, {
      width: '100px', // Adjust width as desired
      padding: '4px',
      border: 'none',
      background: 'transparent',
      cursor: 'pointer'
  });

  transparencySlider.oninput = () => {
      currentOpacity = parseFloat(transparencySlider.value);
      panel.style.background = `rgba(0,0,0,${currentOpacity})`; // Apply new opacity to panel
      localStorage.setItem(OPACITY_KEY, currentOpacity.toString()); // Save the new opacity
  };

  transparencySettingDiv.append(transparencyLabel, transparencySlider);
  settingsPanel.appendChild(transparencySettingDiv);
  // END OPACITY SLIDER FEATURE

  // FEATURE: Profit/Loss Threshold Settings
  const profitTargetSettingDiv = document.createElement('div');
  Object.assign(profitTargetSettingDiv.style, {
      display: 'flex',
      alignItems: 'center',
      gap: '8px',
      marginBottom: '8px'
  });
  const profitTargetLabel = document.createElement('label');
  profitTargetLabel.textContent = 'Profit Target ($):';
  profitTargetLabel.htmlFor = 'profit-target-input';
  Object.assign(profitTargetLabel.style, { flexShrink: '0' });

  const profitTargetInput = document.createElement('input');
  profitTargetInput.type = 'number';
  profitTargetInput.id = 'profit-target-input';
  profitTargetInput.min = '0'; // Can't have negative target
  profitTargetInput.value = profitTarget; // Set initial value
  Object.assign(profitTargetInput.style, {
      width: '80px',
      padding: '4px',
      border: '1px solid #555',
      borderRadius: '4px',
      background: 'rgba(255,255,255,0.1)',
      color: '#fff'
  });
  profitTargetInput.onchange = () => {
      let newValue = parseInt(profitTargetInput.value, 10);
      if (isNaN(newValue) || newValue < 0) newValue = 0;
      profitTarget = newValue;
      profitTargetInput.value = profitTarget;
      localStorage.setItem(PROFIT_TARGET_KEY, profitTarget.toString());
      alertShownProfit = false; // Reset alert shown flag to allow new alert
      localStorage.setItem(ALERT_SHOWN_PROFIT_KEY, 'false');
      refreshAll();
  };
  profitTargetSettingDiv.append(profitTargetLabel, profitTargetInput);
  settingsPanel.appendChild(profitTargetSettingDiv);


  const lossLimitSettingDiv = document.createElement('div');
  Object.assign(lossLimitSettingDiv.style, {
      display: 'flex',
      alignItems: 'center',
      gap: '8px',
      marginBottom: '8px'
  });
  const lossLimitLabel = document.createElement('label');
  lossLimitLabel.textContent = 'Loss Limit ($):';
  lossLimitLabel.htmlFor = 'loss-limit-input';
  Object.assign(lossLimitLabel.style, { flexShrink: '0' });

  const lossLimitInput = document.createElement('input');
  lossLimitInput.type = 'number';
  lossLimitInput.id = 'loss-limit-input';
  lossLimitInput.min = '0'; // Can't have negative limit (it's absolute)
  lossLimitInput.value = lossLimit; // Set initial value
  Object.assign(lossLimitInput.style, {
      width: '80px',
      padding: '4px',
      border: '1px solid #555',
      borderRadius: '4px',
      background: 'rgba(255,255,255,0.1)',
      color: '#fff'
  });
  lossLimitInput.onchange = () => {
      let newValue = parseInt(lossLimitInput.value, 10);
      if (isNaN(newValue) || newValue < 0) newValue = 0;
      lossLimit = newValue;
      lossLimitInput.value = lossLimit;
      localStorage.setItem(LOSS_LIMIT_KEY, lossLimit.toString());
      alertShownLoss = false; // Reset alert shown flag to allow new alert
      localStorage.setItem(ALERT_SHOWN_LOSS_KEY, 'false');
      refreshAll();
  };
  lossLimitSettingDiv.append(lossLimitLabel, lossLimitInput);
  settingsPanel.appendChild(lossLimitSettingDiv);

  const clearAlertsBtn = document.createElement('button');
  clearAlertsBtn.textContent = '✔️ Clear Alerts';
  Object.assign(clearAlertsBtn.style, {
    alignSelf:   'flex-start',
    background:  'rgba(255,255,255,0.1)',
    color:       '#fff',
    border:      'none',
    borderRadius:'6px',
    padding:     '4px 8px',
    cursor:      'pointer',
    marginTop:   '4px'
  });
  clearAlertsBtn.onmouseenter = () => clearAlertsBtn.style.background = 'rgba(255,255,255,0.2)';
  clearAlertsBtn.onmouseleave = () => clearAlertsBtn.style.background = 'rgba(255,255,255,0.1)';
  clearAlertsBtn.onclick = () => {
      alertShownProfit = false;
      alertShownLoss = false;
      localStorage.setItem(ALERT_SHOWN_PROFIT_KEY, 'false');
      localStorage.setItem(ALERT_SHOWN_LOSS_KEY, 'false');
      alertMessageDiv.style.display = 'none'; // Hide current alert immediately
      refreshAll(); // Re-evaluate if any *other* alert should be shown (e.g. if conditions are still met)
  };
  settingsPanel.appendChild(clearAlertsBtn);
  // END THRESHOLD ALERTS FEATURE

  // NEW FEATURE: Mini-Bar Display Count Setting
  const miniBarCountSettingDiv = document.createElement('div');
  Object.assign(miniBarCountSettingDiv.style, {
      display: 'flex',
      alignItems: 'center',
      gap: '8px',
      marginBottom: '8px'
  });
  const miniBarCountLabel = document.createElement('label');
  miniBarCountLabel.textContent = 'Mini-Bar Count:';
  miniBarCountLabel.htmlFor = 'mini-bar-count-input';
  Object.assign(miniBarCountLabel.style, { flexShrink: '0' });

  const miniBarCountInput = document.createElement('input');
  miniBarCountInput.type = 'number';
  miniBarCountInput.id = 'mini-bar-count-input';
  miniBarCountInput.min = '1';
  miniBarCountInput.max = '50'; // Let's cap it at 50 for reasonable display
  miniBarCountInput.value = miniBarCount;
  Object.assign(miniBarCountInput.style, {
      width: '60px',
      padding: '4px',
      border: '1px solid #555',
      borderRadius: '4px',
      background: 'rgba(255,255,255,0.1)',
      color: '#fff'
  });
  miniBarCountInput.onchange = () => {
      let newValue = parseInt(miniBarCountInput.value, 10);
      if (isNaN(newValue) || newValue < 1) newValue = 1;
      if (newValue > 50) newValue = 50; // Cap at 50
      miniBarCount = newValue;
      miniBarCountInput.value = miniBarCount;
      localStorage.setItem(MINI_BAR_COUNT_KEY, miniBarCount.toString());
      refreshAll(); // Refresh to update the mini-bar display
  };
  miniBarCountSettingDiv.append(miniBarCountLabel, miniBarCountInput);
  settingsPanel.appendChild(miniBarCountSettingDiv);
  // END MINI-BAR COUNT FEATURE


  const resetBtn = document.createElement('button');
  resetBtn.textContent = '🔄 Reset Data';
  Object.assign(resetBtn.style, {
    alignSelf:   'flex-start',
    background:  'rgba(255,255,255,0.1)',
    color:       '#fff',
    border:      'none',
    borderRadius:'6px',
    padding:     '4px 8px',
    cursor:      'pointer'
  });
  resetBtn.onmouseenter = () => resetBtn.style.background = 'rgba(255,255,255,0.2)';
  resetBtn.onmouseleave = () => resetBtn.style.background = 'rgba(255,255,255,0.1)';
  resetBtn.onclick = () => {
    if (confirm('Clear all results and reset profit?')) {
      results     = [];
      totalProfit = 0;
      saveResults();
      saveTotalProfit();
      lastPot     = 0;
      roundActive = true;
      hasTracked  = false;
      // Also clear alerts on full reset
      alertShownProfit = false;
      alertShownLoss = false;
      localStorage.setItem(ALERT_SHOWN_PROFIT_KEY, 'false');
      localStorage.setItem(ALERT_SHOWN_LOSS_KEY, 'false');
      refreshAll();
    }
  };
  settingsPanel.appendChild(resetBtn);

  const autoHideBtn = document.createElement('button');
  autoHideBtn.textContent = autoHide ? 'Auto-Hide: On' : 'Auto-Hide: Off';
  Object.assign(autoHideBtn.style, {
    alignSelf:   'flex-start',
    background:  'rgba(255,255,255,0.1)',
    color:       '#fff',
    border:      'none',
    borderRadius:'6px',
    padding:     '4px 8px',
    cursor:      'pointer',
    marginTop:   '4px'
  });
  autoHideBtn.onmouseenter = () => autoHideBtn.style.background = 'rgba(255,255,255,0.2)';
  autoHideBtn.onmouseleave = () => autoHideBtn.style.background = 'rgba(255,255,255,0.1)';
  autoHideBtn.onclick = () => {
    autoHide = !autoHide;
    localStorage.setItem(AUTOHIDE_KEY, JSON.stringify(autoHide));
    autoHideBtn.textContent = autoHide ? 'Auto-Hide: On' : 'Auto-Hide: Off';
    refreshAll();
  };
  settingsPanel.appendChild(autoHideBtn);

  // --- Settings Button for Main Panel ---
  const settingsButton = document.createElement('button');
  settingsButton.textContent = '⚙️ Settings';
  Object.assign(settingsButton.style, {
    alignSelf:   'flex-start',
    background:  'rgba(255,255,255,0.1)',
    color:       '#fff',
    border:      'none',
    borderRadius:'6px',
    padding:     '4px 8px',
    cursor:      'pointer',
    marginTop:   '4px'
  });
  settingsButton.onmouseenter = () => settingsButton.style.background = 'rgba(255,255,255,0.2)';
  settingsButton.onmouseleave = () => settingsButton.style.background = 'rgba(255,255,255,0.1)';
  settingsButton.onclick = () => {
      showSettings = true;
      refreshAll();
  };
  panel.appendChild(settingsButton);

  const saveResults   = () => localStorage.setItem(STORAGE, JSON.stringify(results));
  const saveTotalProfit = () => localStorage.setItem(PROFIT_STORAGE, totalProfit.toString());
  const saveCollapsed = () => localStorage.setItem(COLLAPSE_KEY, JSON.stringify(collapsed));

  // MODIFIED: makeCircle now accepts result, bet, and index for clickable pop-up
  const makeCircle = (result, bet, index) => {
    const container = document.createElement('span');
    Object.assign(container.style, {
      display:         'inline-block',
      width:           '14px', // Slightly larger for better click target/tooltip
      height:          '14px',
      borderRadius:    '50%',
      backgroundColor: result === 'win' ? '#4CAF50' : '#E53935',
      marginRight:     '2px',
      cursor:          'pointer', // Indicate it's clickable
      position:        'relative', // For tooltip positioning
    });

    // Simple tooltip for details on hover
    container.title = `${result.toUpperCase()}: $${bet.toLocaleString()}`;

    // Add click listener for temporary pop-up
    container.addEventListener('click', (e) => {
        e.stopPropagation(); // Prevent potential parent clicks
        
        // Remove any existing temporary popups from this container
        Array.from(container.children).forEach(child => {
            if (child.classList.contains('rr-temp-popup')) {
                container.removeChild(child);
            }
        });

        const tempPopup = document.createElement('div');
        tempPopup.classList.add('rr-temp-popup'); // Add a class for easier identification
        tempPopup.textContent = `${result.toUpperCase()}: $${bet.toLocaleString()}`;
        Object.assign(tempPopup.style, {
            position: 'absolute',
            background: 'rgba(0,0,0,0.9)', // Slightly darker for better contrast
            border: '1px solid #555',
            color: 'white',
            padding: '4px 8px',
            borderRadius: '4px',
            fontSize: '12px',
            whiteSpace: 'nowrap',
            zIndex: '10000000',
            top: '-28px', // Position above the circle
            left: '50%',
            transform: 'translateX(-50%)',
            pointerEvents: 'none', // Prevent pop-up itself from blocking clicks
            opacity: '0', // Start invisible
            transition: 'opacity 0.2s ease-in-out', // Smooth fade in/out
        });
        container.appendChild(tempPopup);

        // Fade in
        setTimeout(() => tempPopup.style.opacity = '1', 10); // Small delay to allow render

        // Fade out and remove
        setTimeout(() => {
            tempPopup.style.opacity = '0';
            setTimeout(() => {
                if (container.contains(tempPopup)) {
                    container.removeChild(tempPopup);
                }
            }, 200); // Allow transition to finish before removing
        }, 1500); // Display for 1.5 seconds
    });

    return container;
  };

  function updateStatus() {
    statusDiv.textContent = collapsed ? '▪' : (roundActive ? '►' : '▸');
  }

  function updatePanelVisibility() {
    if (!autoHide) {
      panel.style.display = 'flex';
      return;
    }
    const onMenu = document.body.innerText.includes('Password');
    panel.style.display = onMenu ? 'flex' : 'none';
  }

  function refreshAll() {
    // NEW: Alert Logic - Hide by default before checking
    alertMessageDiv.style.display = 'none';

    // Check for Profit Target
    if (profitTarget > 0 && totalProfit >= profitTarget && !alertShownProfit) {
        alertMessageDiv.textContent = `🎯 PROFIT TARGET REACHED! +$${profitTarget.toLocaleString()}`;
        Object.assign(alertMessageDiv.style, {
            background: 'rgba(76, 175, 80, 0.8)', // Green
            borderColor: '#4CAF50',
            display: 'block'
        });
        alertShownProfit = true;
        localStorage.setItem(ALERT_SHOWN_PROFIT_KEY, 'true');
    }
    // Check for Loss Limit
    else if (lossLimit > 0 && totalProfit <= -lossLimit && !alertShownLoss) {
        alertMessageDiv.textContent = `🚨 LOSS LIMIT REACHED! -$${lossLimit.toLocaleString()}`;
        Object.assign(alertMessageDiv.style, {
            background: 'rgba(229, 57, 53, 0.8)', // Red
            borderColor: '#E53935',
            display: 'block'
        });
        alertShownLoss = true;
        localStorage.setItem(ALERT_SHOWN_LOSS_KEY, 'true');
    }

    const sign   = totalProfit >= 0 ? '+' : '–';

    profitMini.textContent = `${sign}${Math.abs(totalProfit).toLocaleString()}`;
    profitMini.style.color = totalProfit >= 0 ? '#4CAF50' : '#E53935';

    profitDiv.textContent  = `💰 Profit: ${sign}$${Math.abs(totalProfit).toLocaleString()}`;
    profitDiv.style.color  = totalProfit >= 0 ? '#4CAF50' : '#E53935';

    const wins = results.filter(r => r.result==='win').length;
    const tot  = results.length;
    winrateDiv.textContent = `🎯 Win Rate: ${tot?((wins/tot)*100).toFixed(1):'0.0'}% (${wins}/${tot})`;

    let w=0,l=0;
    for (const r of results) {
      if (r.result==='win') { if(l) break; w++; }
      else                  { if(w) break; l++; }
    }
    streakDiv.textContent = w?`🔥 Streak: ${w}`: l?`💀 Streak: ${l}`:'⏸️ No streak';

    resultsContainer.innerHTML = '';
    // Display only up to maxDisplayMatches in expanded view
    results.slice(0, maxDisplayMatches).forEach((r,i) => {
      const row = document.createElement('div');
      row.append(
        makeCircle(r.result, r.bet, i), // Pass data for clickable circle
        document.createTextNode(`${i+1}. ${r.result.toUpperCase()} — $${r.bet.toLocaleString()}`)
      );
      resultsContainer.appendChild(row);
    });

    miniBar.innerHTML = '';
    // MODIFIED: Mini-bar now uses miniBarCount for compact view
    results.slice(0, miniBarCount).forEach((r, i) => miniBar.append(makeCircle(r.result, r.bet, i)));


    // --- Visibility Logic for Main Panel vs. Settings Panel ---
    if (showSettings) {
        statsGroup.style.display       = 'none';
        settingsButton.style.display   = 'none';
        settingsPanel.style.display    = 'flex';
        miniBar.style.display          = 'none';
        profitMini.style.display       = 'none';
        alertMessageDiv.style.display  = 'none'; // Hide alert when in settings
    } else if (collapsed) {
        miniBar.style.display          = 'flex';
        profitMini.style.display       = 'block';
        statsGroup.style.display       = 'none';
        settingsButton.style.display   = 'none';
        settingsPanel.style.display    = 'none';
        // Alert can remain visible in collapsed state if active
    } else {
        miniBar.style.display          = 'none';
        profitMini.style.display       = 'none';
        statsGroup.style.display       = 'flex';
        settingsButton.style.display   = 'inline-block';
        settingsPanel.style.display    = 'none';
        // Alert can remain visible in expanded state if active
    }

    updateStatus();
    updatePanelVisibility();
  }

  function addResult(type) {
    if (!roundActive) return;

    if (type === 'win') {
        totalProfit += lastPot;
    } else {
        totalProfit -= lastPot;
    }
    saveTotalProfit();

    results.unshift({ result: type, bet: lastPot });
    // Now truncate based on maxDisplayMatches
    if (results.length > maxDisplayMatches) {
      results.pop();
    }
    saveResults();
    roundActive = false;
    hasTracked  = true;

    // NEW: Reset alert shown flags if profit moves away from threshold
    if (profitTarget > 0 && alertShownProfit && totalProfit < profitTarget) {
        alertShownProfit = false;
        localStorage.setItem(ALERT_SHOWN_PROFIT_KEY, 'false');
    }
    if (lossLimit > 0 && alertShownLoss && totalProfit > -lossLimit) {
        alertShownLoss = false;
        localStorage.setItem(ALERT_SHOWN_LOSS_KEY, 'false');
    }

    refreshAll();
  }
  function scanPot() {
    document.querySelectorAll('body *').forEach(el => {
      const txt = el.innerText?.trim();
      if (txt?.includes('POT MONEY:$')) {
        const m = txt.match(/POT MONEY:\$\s*([\d,]+)/);
        if (m) lastPot = Math.floor(parseInt(m[1].replace(/,/g,''),10)/2);
      }
    });
  }
  function scanResult() {
    if (!roundActive) return;
    document.querySelectorAll('body *').forEach(el => {
      const txt = el.innerText?.trim();
      if (txt?.includes('You take your winnings')) addResult('win');
      if (txt?.includes('BANG! You fall down'))    addResult('lose');
    });
  }
  function scanStart() {
    if (hasTracked && document.body.innerText.includes('Waiting:')) {
      roundActive = true;
      hasTracked  = false;
      updateStatus();
    }
  }

  (function() {
    let mx, my;
    const savePos = () => localStorage.setItem(POS_KEY, JSON.stringify({
      top: panel.style.top, left: panel.style.left
    }));
    dragHandle.addEventListener('mousedown', e => {
      e.preventDefault(); mx = e.clientX; my = e.clientY;
      document.addEventListener('mousemove', onMove);
      document.addEventListener('mouseup', onUp);
      function onMove(ev) {
        const dx = ev.clientX - mx, dy = ev.clientY - my;
        mx = ev.clientX; my = ev.clientY;
        panel.style.top  = panel.offsetTop + dy + 'px';
        panel.style.left = panel.offsetLeft + dx + 'px';
      }
      function onUp() {
        document.removeEventListener('mousemove', onMove);
        document.removeEventListener('mouseup', onUp);
        savePos();
      }
    });
    dragHandle.addEventListener('touchstart', e => {
      mx = e.touches[0].clientX; my = e.touches[0].clientY;
      document.addEventListener('touchmove', onTmove);
      document.addEventListener('touchend', onTend);
      function onTmove(ev) {
        const dx = ev.touches[0].clientX - mx, dy = ev.touches[0].clientY - my;
        mx = ev.touches[0].clientX; my = ev.touches[0].clientY;
        panel.style.top  = panel.offsetTop + dy + 'px';
        panel.style.left = panel.offsetLeft + dx + 'px';
      }
      function onTend() {
        document.removeEventListener('touchmove', onTmove);
        document.removeEventListener('touchend', onTend);
        savePos();
      }
    });
  })();

  statusDiv.addEventListener('click', () => {
    collapsed = !collapsed;
    if (showSettings && collapsed) { // If settings are open and collapsing, close settings
        showSettings = false;
    }
    localStorage.setItem(COLLAPSE_KEY, JSON.stringify(collapsed));
    refreshAll();
  });

  // Dismiss alert by clicking on the alert message itself
  alertMessageDiv.addEventListener('click', () => {
      alertMessageDiv.style.display = 'none';
  });


  refreshAll();
  setInterval(() => {
    updatePanelVisibility();
    scanStart();
    scanPot();
    scanResult();
  }, 500);

})();