Perplexity MAX

Free Max & Pro features for Perplexity.ai

// ==UserScript==
// @name         Perplexity MAX
// @description  Free Max & Pro features for Perplexity.ai
// @version      2.0
// @author       AntiKeks & d4nc3r
// @license      MIT
// @match        https://*.perplexity.ai/*
// @run-at       document-start
// @grant        none
// @namespace https://greasyfork.org/users/1497368
// ==/UserScript==

(function () {
  'use strict';

  /* ─────────────────────────── LOGGING ─────────────────────────── */
  const DEBUG = true;  // Enable debug logging for troubleshooting
  const VERBOSE = false; // Disable verbose logging to improve performance
  const log = (...args) => DEBUG && console.log('[PERPLEXITY-MAX]', ...args);
  const verbose = (...args) => VERBOSE && console.log('[PERPLEXITY-MAX-VERBOSE]', ...args);
  log('Initialization complete - MAX features activated');

  /* ───────────────────────── THROTTLING UI ────────────────────── */
  const COOLDOWN = 1000;              // Minimum interval between React "pings" (ms)
  let lastUpdate = 0;

  function forceReactUpdate() {
    const now = Date.now();
    if (now - lastUpdate < COOLDOWN) return;   // call no more than once per COOLDOWN
    lastUpdate = now;

    requestAnimationFrame(() => {
      document.dispatchEvent(new Event('visibilitychange'));
      window.dispatchEvent(new Event('resize'));
      log('Dispatched events to update UI');
    });
  }

  /* ─────────────────────── PATCH USER DATA ───────────────── */
  function patchDataRecursively(obj, path = '', depth = 0) {
    // Limit recursion depth to avoid performance issues
    if (depth > 5) return false;

    // Skip null, non-objects, and React elements
    if (!obj || typeof obj !== 'object' || obj.$$typeof) return false;

    let changed = false;

    // Check for subscription-related fields directly
    if (obj.hasOwnProperty('subscription_tier') && obj.subscription_tier !== 'max') {
      obj.subscription_tier = 'max';
      changed = true;
      log(`${path}.subscription_tier → max (was: ${obj.subscription_tier})`);
    }

    if (obj.hasOwnProperty('subscription_source') && obj.subscription_source !== 'stripe') {
      const oldValue = obj.subscription_source;
      obj.subscription_source = 'stripe';
      changed = true;
      log(`${path}.subscription_source → stripe (was: ${oldValue})`);
    }

    if (obj.hasOwnProperty('subscription_status') && obj.subscription_status !== 'active') {
      const oldValue = obj.subscription_status;
      obj.subscription_status = 'active';
      changed = true;
      log(`${path}.subscription_status → active (was: ${oldValue})`);
    }

    // Handle pro/max flags - IMPORTANT: For MAX to show, we need both is_pro=true AND is_max=true
    if (obj.hasOwnProperty('is_pro')) {
      const oldValue = obj.is_pro;
      obj.is_pro = true;  // Set to true for MAX to work properly
      if (oldValue !== true) {
        changed = true;
        log(`${path}.is_pro → true (was: ${oldValue})`);
      }
    }

    if (obj.hasOwnProperty('is_max')) {
      if (obj.is_max !== true) {
        obj.is_max = true;
        changed = true;
        log(`${path}.is_max → true`);
      }
    }

    if (obj.hasOwnProperty('has_max')) {
      if (obj.has_max !== true) {
        obj.has_max = true;
        changed = true;
        log(`${path}.has_max → true`);
      }
    }

    // Also handle has_pro flag
    if (obj.hasOwnProperty('has_pro')) {
      if (obj.has_pro !== true) {
        obj.has_pro = true;
        changed = true;
        log(`${path}.has_pro → true`);
      }
    }

    // Handle rate limits directly - very important!
    if (obj.hasOwnProperty('remaining_pro') && obj.remaining_pro !== 999999) {
      obj.remaining_pro = 999999;
      changed = true;
      log(`${path}.remaining_pro → 999999 (unlimited)`);
    }

    if (obj.hasOwnProperty('remaining_research') && obj.remaining_research !== 999999) {
      obj.remaining_research = 999999;
      changed = true;
      log(`${path}.remaining_research → 999999 (unlimited)`);
    }

    if (obj.hasOwnProperty('remaining_labs') && obj.remaining_labs !== 999999) {
      obj.remaining_labs = 999999;
      changed = true;
      log(`${path}.remaining_labs → 999999 (unlimited)`);
    }

    // Handle upload-related fields
    if (obj.hasOwnProperty('rate_limited') && obj.rate_limited !== false) {
      obj.rate_limited = false;
      changed = true;
      log(`${path}.rate_limited → false (enabling uploads)`);
    }

    if (obj.hasOwnProperty('remaining_uploads') && obj.remaining_uploads !== 999999) {
      obj.remaining_uploads = 999999;
      changed = true;
      log(`${path}.remaining_uploads → 999999 (unlimited)`);
    }

    if (obj.hasOwnProperty('max_uploads') && obj.max_uploads < 100) {
      obj.max_uploads = 100;
      changed = true;
      log(`${path}.max_uploads → 100`);
    }

    if (obj.hasOwnProperty('upload_limit') && obj.upload_limit < 100) {
      obj.upload_limit = 100;
      changed = true;
      log(`${path}.upload_limit → 100`);
    }

    if (obj.hasOwnProperty('can_upload') && obj.can_upload !== true) {
      obj.can_upload = true;
      changed = true;
      log(`${path}.can_upload → true`);
    }

    // Only process important objects further
    if (obj.hasOwnProperty('subscription') && typeof obj.subscription === 'object' && obj.subscription !== null) {
      if (!obj.subscription) {
        obj.subscription = {
          tier: 'max',
          status: 'active',
          source: 'stripe',
          price: 200,
          billing_period: 'monthly'
        };
        changed = true;
        log(`${path}.subscription created with MAX tier`);
      } else {
        if (obj.subscription.tier !== 'max') {
          obj.subscription.tier = 'max';
          changed = true;
        }
        if (obj.subscription.status !== 'active') {
          obj.subscription.status = 'active';
          changed = true;
        }
        if (obj.subscription.source !== 'stripe') {
          obj.subscription.source = 'stripe';
          changed = true;
        }
      }
    }

    // Handle features object
    if (obj.hasOwnProperty('features') && typeof obj.features === 'object' && obj.features !== null) {
      const featuresToEnable = {
        max_models: true,
        claude_opus_access: true,
        gpt_4o_access: true,
        unlimited_searches: true,
        unlimited_uploads: true,
        all_models: true,
        image_upload: true,
        file_upload: true
      };

      for (const f in featuresToEnable) {
        if (obj.features[f] !== true) {
          obj.features[f] = true;
          changed = true;
        }
      }
    }

    // Recursively process child objects - but only important ones to avoid performance issues
    for (const key in obj) {
      if (Object.prototype.hasOwnProperty.call(obj, key) && typeof obj[key] === 'object' && obj[key] !== null) {
        // Only process important keys to improve performance
        const importantKeys = [
          'user', 'account', 'subscription', 'features', 'settings', 'config',
          'data', 'profile', 'preferences', 'options', 'state', 'models',
          'uploads', 'files', 'images', 'usage'
        ];

        if (importantKeys.includes(key) || key.includes('user') || key.includes('subscription') ||
            key.includes('model') || key.includes('upload') || key.includes('file')) {
          if (patchDataRecursively(obj[key], path ? `${path}.${key}` : key, depth + 1)) {
            changed = true;
          }
        }
      }
    }

    return changed;
  }

  /* ──────────────────── PATCH GLOBAL JSON.parse ───────────────── */
  const originalParse = JSON.parse;
  JSON.parse = function (text, reviver) {
    const data = originalParse.call(this, text, reviver);

    if (patchDataRecursively(data)) {
      log('JSON-object converted to Max');
      forceReactUpdate();
    }
    return data;
  };

  /* ──────────────────────────── PATCH fetch ─────────────────────── */
  const originalFetch = window.fetch;
  window.fetch = async function (input, init) {
    try {
      // Extract URL and method
      const url = typeof input === 'string' ? input : input.url;
      const method = init?.method || 'GET';

      // Log all requests in debug mode
      if (DEBUG) {
        verbose(`Fetch request: ${method} ${url}`);
      }

      // Force logging for important endpoints
      if (url && (
        url.includes('/rest/rate-limit/all') ||
        url.includes('/rest/user/save-settings') ||
        url.includes('/rest/uploads/create_upload_url') ||
        url.includes('/rest/sse/perplexity_ask')
      )) {
        log(`Intercepting important request: ${method} ${url}`);
      }

      // Block Datadog analytics
      if (url && url.includes('datadoghq.com')) {
        log('Blocked Datadog analytics request');
        // Return empty success response
        return new Response(JSON.stringify({ success: true }), {
          status: 200,
          statusText: 'OK',
          headers: {
            'Content-Type': 'application/json'
          }
        });
      }

      // Block other analytics services
      if (url && (
        url.includes('analytics') ||
        url.includes('telemetry') ||
        url.includes('metrics') ||
        url.includes('singular.net') ||
        url.includes('segment.io') ||
        url.includes('mixpanel') ||
        url.includes('facebook.com') ||
        url.includes('fbevents')
      )) {
        log(`Blocked analytics request: ${url}`);
        // Return empty success response
        return new Response(JSON.stringify({ success: true }), {
          status: 200,
          statusText: 'OK',
          headers: {
            'Content-Type': 'application/json'
          }
        });
      }

      // CRITICAL: Handle rate-limit requests - MUST intercept these
      if (url && url.includes('/rest/rate-limit/all')) {
        log(`Intercepted rate-limit request (${method}) - FORCING UNLIMITED LIMITS`);

        // ALWAYS return our custom response with unlimited limits
        const unlimitedResponse = {
          remaining_pro: 999999,
          remaining_research: 999999,
          remaining_labs: 999999,
          remaining_uploads: 999999,
          remaining_copilot: 999999,
          remaining_search: 999999,
          remaining_create: 999999,
          rate_limited: false
        };

        log('Returning unlimited rate limits');

        // Return our custom response directly, bypassing the server
        return new Response(JSON.stringify(unlimitedResponse), {
          status: 200,
          statusText: 'OK',
          headers: {
            'Content-Type': 'application/json'
          }
        });
      }

      // Handle SSE requests for perplexity_ask
      if (url && url.includes('/rest/sse/perplexity_ask')) {
        log(`Intercepted SSE perplexity_ask request (${method})`);

        try {
          // Get the original request body
          let requestBody = {};
          if (init && init.body) {
            if (typeof init.body === 'string') {
              requestBody = JSON.parse(init.body);
            }
          }

          // Modify params to ensure MAX features
          if (requestBody.params) {
            // Enable MAX model options
            requestBody.params.model_preference = requestBody.params.model_preference || 'claude-3-opus-20240229';

            // Add MAX subscription flags
            requestBody.params.is_max = true;
            requestBody.params.has_max = true;
            requestBody.params.is_pro = true;
            requestBody.params.subscription_tier = 'max';

            // Set to use the best model available
            if (!requestBody.params.model_preference || requestBody.params.model_preference === 'turbo') {
              requestBody.params.model_preference = 'claude-3-opus-20240229';
              log('Enhanced model to claude-3-opus');
            }

            // Update the request with modified body
            init.body = JSON.stringify(requestBody);
            log('Modified SSE request with MAX subscription data');
          }

          // Make the modified request
          const response = await originalFetch(input, init);

          // For SSE responses, we need to create a custom response that modifies the stream
          if (response.headers.get('content-type')?.includes('text/event-stream')) {
            log('Intercepting SSE response stream');

            const reader = response.body.getReader();
            const stream = new ReadableStream({
              async start(controller) {
                try {
                  while (true) {
                    const { done, value } = await reader.read();
                    if (done) {
                      controller.close();
                      break;
                    }

                    // Convert the chunk to text
                    let text = new TextDecoder().decode(value);

                    // Process each event in the chunk
                    const events = text.split('\n\n');
                    let modifiedEvents = [];

                    for (const event of events) {
                      if (!event.trim()) continue;

                      // Check if this is a data event
                      if (event.includes('event: message') && event.includes('data: {')) {
                        try {
                          // Extract the data part
                          const dataMatch = event.match(/data: ({.*})/);
                          if (dataMatch && dataMatch[1]) {
                            const data = JSON.parse(dataMatch[1]);

                            // Modify the data to ensure MAX features
                            data.gpt4 = true; // Enable GPT-4 flag
                            data.display_model = data.display_model || 'claude-3-opus-20240229'; // Set display model to MAX

                            // If there's a classifier_results object, modify it
                            if (data.classifier_results) {
                              data.classifier_results.personal_search = true;
                              data.classifier_results.skip_search = false;
                            }

                            // If there's a telemetry_data object, modify it
                            if (data.telemetry_data) {
                              data.telemetry_data.is_pro = true;
                              data.telemetry_data.is_max = true;
                              data.telemetry_data.has_max = true;
                            }

                            // Replace the data in the event
                            const modifiedEvent = event.replace(dataMatch[1], JSON.stringify(data));
                            modifiedEvents.push(modifiedEvent);
                            continue;
                          }
                        } catch (e) {
                          verbose('Error modifying SSE event:', e);
                        }
                      }

                      // If we couldn't modify the event, keep it as is
                      modifiedEvents.push(event);
                    }

                    // Join the events back together
                    const modifiedText = modifiedEvents.join('\n\n') + '\n\n';
                    controller.enqueue(new TextEncoder().encode(modifiedText));
                  }
                } catch (e) {
                  log('Error in SSE stream processing:', e);
                  controller.error(e);
                }
              }
            });

            // Return a new response with the modified stream
            return new Response(stream, {
              headers: response.headers,
              status: response.status,
              statusText: response.statusText
            });
          }

          return response;
        } catch (e) {
          log('Error processing SSE request:', e);
          // If there's an error, proceed with the original request
          return originalFetch(input, init);
        }
      }

      // Handle upload URL creation requests
      if (url && url.includes('/rest/uploads/create_upload_url')) {
        log('Intercepted upload URL creation request');

        // Make the original request
        const response = await originalFetch(input, init);

        try {
          // Clone the response so we can read and modify it
          const cloned = response.clone();
          const json = await cloned.json();

          // Remove rate limiting for uploads
          if (json.hasOwnProperty('rate_limited')) {
            json.rate_limited = false;
            log('.rate_limited → false (enabling uploads)');

            // Return the modified response
            return new Response(JSON.stringify(json), {
              status: response.status,
              statusText: response.statusText,
              headers: response.headers
            });
          }

          // If we didn't modify anything, return the original response
          return response;
        } catch (e) {
          log('Error processing upload URL response:', e);
          // Not JSON or other error - return original response
          return response;
        }
      }

      // Handle feedback visibility requests
      if (url && url.includes('/rest/entry/should-show-feedback')) {
        log('Intercepted feedback visibility request');

        // Create a modified response that always sets should_show_feedback to true
        return new Response(JSON.stringify({
          should_show_feedback: true
        }), {
          status: 200,
          statusText: 'OK',
          headers: {
            'Content-Type': 'application/json'
          }
        });
      }

      // Handle save-settings requests
      if (url && url.includes('/rest/user/save-settings')) {
        log(`Intercepted save-settings request (${method})`);

        // For GET requests, we'll handle the response below
        if (method === 'GET') {
          const response = await originalFetch(input, init);
          try {
            const cloned = response.clone();
            const json = await cloned.json();

            // Modify the settings to ensure MAX subscription
            json.subscription_tier = 'max';
            json.subscription_status = 'active';
            json.subscription_source = 'stripe';
            json.stripe_status = 'active';
            json.gpt4_limit = 999999;
            json.pplx_alpha_limit = 999999;
            json.pplx_beta_limit = 999999;
            json.pages_limit = 999999;
            json.upload_limit = 999999;
            json.create_limit = 999999;
            json.article_image_upload_limit = 999999;
            json.max_files_per_user = 999999;
            json.max_files_per_repository = 999999;

            log('Modified GET save-settings response with MAX subscription data');

            // Return the modified response
            return new Response(JSON.stringify(json), {
              status: response.status,
              statusText: response.statusText,
              headers: response.headers
            });
          } catch (e) {
            log('Error processing GET save-settings response:', e);
            return response;
          }
        }

        // For PUT/POST requests, modify both the request and response
        if (method === 'PUT' || method === 'POST') {
          try {
            // Get the original request body
            let requestBody = {};
            if (init && init.body) {
              if (typeof init.body === 'string') {
                requestBody = JSON.parse(init.body);
              } else if (init.body instanceof FormData) {
                // Handle FormData if needed
              }
            }

            // Check if this is a partial update with updated_settings
            if (requestBody.updated_settings) {
              log('Detected partial settings update');

              // Ensure MAX subscription settings in the updated_settings
              requestBody.updated_settings.subscription_tier = 'max';
              requestBody.updated_settings.subscription_status = 'active';
              requestBody.updated_settings.subscription_source = 'stripe';
              requestBody.updated_settings.stripe_status = 'active';
              requestBody.updated_settings.gpt4_limit = 999999;
              requestBody.updated_settings.pplx_alpha_limit = 999999;
              requestBody.updated_settings.pplx_beta_limit = 999999;
              requestBody.updated_settings.pages_limit = 999999;
              requestBody.updated_settings.upload_limit = 999999;
              requestBody.updated_settings.create_limit = 999999;
              requestBody.updated_settings.article_image_upload_limit = 999999;
              requestBody.updated_settings.max_files_per_user = 999999;
              requestBody.updated_settings.max_files_per_repository = 999999;
            } else {
              // Ensure MAX subscription settings in the full settings
              requestBody.subscription_tier = 'max';
              requestBody.subscription_status = 'active';
              requestBody.subscription_source = 'stripe';
              requestBody.stripe_status = 'active';
              requestBody.gpt4_limit = 999999;
              requestBody.pplx_alpha_limit = 999999;
              requestBody.pplx_beta_limit = 999999;
              requestBody.pages_limit = 999999;
              requestBody.upload_limit = 999999;
              requestBody.create_limit = 999999;
              requestBody.article_image_upload_limit = 999999;
              requestBody.max_files_per_user = 999999;
              requestBody.max_files_per_repository = 999999;
            }

            // Update the request with modified body
            const modifiedInit = {
              ...init,
              body: JSON.stringify(requestBody)
            };

            log('Modified save-settings request with MAX subscription data');

            // Make the modified request
            const response = await originalFetch(input, modifiedInit);

            // Also modify the response to ensure MAX settings are returned
            try {
              const cloned = response.clone();
              const json = await cloned.json();

              // Always ensure the response has MAX settings
              json.subscription_tier = 'max';
              json.subscription_status = 'active';
              json.subscription_source = 'stripe';
              json.stripe_status = 'active';
              json.gpt4_limit = 999999;
              json.pplx_alpha_limit = 999999;
              json.pplx_beta_limit = 999999;
              json.pages_limit = 999999;
              json.upload_limit = 999999;
              json.create_limit = 999999;
              json.article_image_upload_limit = 999999;
              json.max_files_per_user = 999999;
              json.max_files_per_repository = 999999;

              // Log the modified response for debugging
              log('Modified PUT/POST save-settings response with MAX subscription data');

              return new Response(JSON.stringify(json), {
                status: response.status,
                statusText: response.statusText,
                headers: response.headers
              });
            } catch (e) {
              // If there's an error processing the response, return the original
              log('Error modifying save-settings response: ' + e.message);
              return response;
            }
          } catch (e) {
            log('Error processing save-settings request: ' + e.message);
            // If there's an error, proceed with the original request
            return originalFetch(input, init);
          }
        }
      }

      // For all other requests, proceed normally
      const response = await originalFetch(input, init);

      // Check if the response is JSON and modify it if needed
      try {
        // Check if the response is actually JSON
        const ct = response.headers.get('content-type') || '';
        if (!ct.startsWith('application/json')) return response;

        const cloned = response.clone();
        const json = await cloned.json();

        if (patchDataRecursively(json)) {
          log(`Response modified ← ${url}`);
          forceReactUpdate();
          return new Response(JSON.stringify(json), {
            status: response.status,
            statusText: response.statusText,
            headers: response.headers
          });
        }
      } catch (e) {
        /* not JSON — ignore */
      }

      return response;
    } catch (e) {
      log('Error in fetch interceptor:', e);
      // If there's a critical error, fall back to original fetch
      return originalFetch(input, init);
    }
  };

  // Also inject the fetch interceptor into the page context
  const injectFetchInterceptor = () => {
    const script = document.createElement('script');
    script.textContent = `
      (function() {
        console.log('[PERPLEXITY-MAX-INJECTED] Setting up fetch interceptor');

        const originalFetch = window.fetch;
        window.fetch = async function(input, init) {
          try {
            // Extract URL and method
            const url = typeof input === 'string' ? input : input.url;
            const method = init?.method || 'GET';

            // CRITICAL: Handle rate-limit requests - MUST intercept these
            if (url && url.includes('/rest/rate-limit/all')) {
              console.log('[PERPLEXITY-MAX-INJECTED] Intercepted rate-limit request - FORCING UNLIMITED LIMITS');

              // ALWAYS return our custom response with unlimited limits
              const unlimitedResponse = {
                remaining_pro: 999999,
                remaining_research: 999999,
                remaining_labs: 999999,
                remaining_uploads: 999999,
                remaining_copilot: 999999,
                remaining_search: 999999,
                remaining_create: 999999,
                rate_limited: false
              };

              // Return our custom response directly, bypassing the server
              return new Response(JSON.stringify(unlimitedResponse), {
                status: 200,
                statusText: 'OK',
                headers: {
                  'Content-Type': 'application/json'
                }
              });
            }

            // Handle save-settings requests
            if (url && url.includes('/rest/user/save-settings')) {
              console.log('[PERPLEXITY-MAX-INJECTED] Intercepted save-settings request');

              // For GET requests, we'll handle the response
              if (method === 'GET') {
                const response = await originalFetch(input, init);
                try {
                  const cloned = response.clone();
                  const json = await cloned.json();

                  // Modify the settings to ensure MAX subscription
                  json.subscription_tier = 'max';
                  json.subscription_status = 'active';
                  json.subscription_source = 'stripe';
                  json.stripe_status = 'active';
                  json.gpt4_limit = 999999;
                  json.pplx_alpha_limit = 999999;
                  json.pplx_beta_limit = 999999;
                  json.pages_limit = 999999;
                  json.upload_limit = 999999;
                  json.create_limit = 999999;
                  json.article_image_upload_limit = 999999;
                  json.max_files_per_user = 999999;
                  json.max_files_per_repository = 999999;

                  console.log('[PERPLEXITY-MAX-INJECTED] Modified GET save-settings response');

                  // Return the modified response
                  return new Response(JSON.stringify(json), {
                    status: response.status,
                    statusText: response.statusText,
                    headers: response.headers
                  });
                } catch (e) {
                  console.error('[PERPLEXITY-MAX-INJECTED] Error processing GET save-settings response:', e);
                  return response;
                }
              }
            }

            // For all other requests, proceed normally
            return originalFetch.apply(this, arguments);
          } catch (e) {
            console.error('[PERPLEXITY-MAX-INJECTED] Error in fetch interceptor:', e);
            // If there's a critical error, fall back to original fetch
            return originalFetch.apply(this, arguments);
          }
        };

        console.log('[PERPLEXITY-MAX-INJECTED] Fetch interceptor setup complete');
      })();
    `;

    document.head.appendChild(script);
    document.head.removeChild(script);
  };

  /* ───────────────────── SIMPLIFIED WebSocket HANDLING ──────────────── */
  // This is a simplified version that just logs connections but doesn't modify data
  // This avoids breaking the WebSocket connections
  const originalWebSocket = window.WebSocket;
  window.WebSocket = function(url, protocols) {
    log(`WebSocket connection to ${url}`);
    return new originalWebSocket(url, protocols);
  };

  /* ─────────────── BLOCK ANALYTICS AND TRACKING SCRIPTS ─────────────── */
  function blockAnalyticsScripts() {
    log('Setting up analytics and tracking script blocker');

    // Override XMLHttpRequest to block analytics
    const originalXhrOpen = XMLHttpRequest.prototype.open;
    const originalXhrSend = XMLHttpRequest.prototype.send;
    const originalXhrSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;

    // Fix for XMLHttpRequest state issues
    XMLHttpRequest.prototype.open = function(method, url, ...rest) {
      // Store the state and URL for later use
      this._pplxState = 'OPENED';
      this._pplxUrl = url;

      if (typeof url === 'string' && (
        url.includes('datadoghq.com') ||
        url.includes('analytics') ||
        url.includes('telemetry') ||
        url.includes('metrics') ||
        url.includes('singular.net') ||
        url.includes('segment.io') ||
        url.includes('mixpanel') ||
        url.includes('facebook') ||
        url.includes('fbevents')
      )) {
        log(`Blocked XHR analytics request to: ${url}`);
        // Make this XHR do nothing but maintain proper state
        this._pplxBlocked = true;

        // Create a safe version that maintains proper state
        this.setRequestHeader = function(header, value) {
          // Allow setRequestHeader to be called without errors
          if (this._pplxState === 'OPENED') {
            // Just log but don't actually set headers
            verbose(`Blocked setRequestHeader for ${url}: ${header}=${value}`);
            return;
          } else {
            throw new DOMException('XMLHttpRequest.setRequestHeader: XMLHttpRequest state must be OPENED.');
          }
        };

        this.send = function(body) {
          // Change state to prevent further calls
          this._pplxState = 'DONE';

          // Simulate a successful response
          setTimeout(() => {
            if (typeof this.onreadystatechange === 'function') {
              this.readyState = 4;
              this.status = 200;
              this.response = '{"success":true}';
              this.responseText = '{"success":true}';
              this.onreadystatechange();
            }

            if (typeof this.onload === 'function') {
              this.onload();
            }
          }, 10);
        };

        // Still call original open to maintain proper state
        return originalXhrOpen.call(this, method, url, ...rest);
      }

      // For non-blocked requests, proceed normally
      return originalXhrOpen.call(this, method, url, ...rest);
    };

    // Safer setRequestHeader override
    XMLHttpRequest.prototype.setRequestHeader = function(header, value) {
      try {
        // Only proceed if not blocked
        if (!this._pplxBlocked) {
          return originalXhrSetRequestHeader.call(this, header, value);
        } else {
          // For blocked requests, just pretend it worked
          verbose(`Blocked setRequestHeader: ${header}=${value}`);
          return;
        }
      } catch (e) {
        log(`Error in setRequestHeader: ${e.message}`);
        // If there's an error, don't break the page
        return;
      }
    };

    // Safer send override
    XMLHttpRequest.prototype.send = function(body) {
      try {
        // Only proceed if not blocked
        if (!this._pplxBlocked) {
          return originalXhrSend.call(this, body);
        } else {
          // For blocked requests, simulate success
          this._pplxState = 'DONE';
          setTimeout(() => {
            if (typeof this.onreadystatechange === 'function') {
              this.readyState = 4;
              this.status = 200;
              this.response = '{"success":true}';
              this.responseText = '{"success":true}';
              this.onreadystatechange();
            }

            if (typeof this.onload === 'function') {
              this.onload();
            }
          }, 10);
          return;
        }
      } catch (e) {
        log(`Error in send: ${e.message}`);
        // If there's an error, don't break the page
        return;
      }
    };

    // Inject script to block analytics in page context
    const script = document.createElement('script');
    script.textContent = `
      (function() {
        console.log('[PERPLEXITY-MAX-INJECTED] Setting up analytics blocker');

        // Block Datadog
        window.DD_RUM = {
          init: function() { console.log('[PERPLEXITY-MAX-INJECTED] Blocked Datadog init'); },
          addTiming: function() {},
          addAction: function() {},
          addError: function() {},
          startSessionReplayRecording: function() {},
          setGlobalContextProperty: function() {}
        };

        // Block other analytics services
        window.ga = function() { console.log('[PERPLEXITY-MAX-INJECTED] Blocked GA call'); };
        window.gtag = function() { console.log('[PERPLEXITY-MAX-INJECTED] Blocked GTM call'); };
        window._paq = { push: function() { console.log('[PERPLEXITY-MAX-INJECTED] Blocked Matomo call'); } };
        window.mixpanel = { track: function() {}, identify: function() {} };
        window.segment = { track: function() {}, identify: function() {} };
        window.amplitude = { track: function() {}, identify: function() {} };

        // Block Facebook Pixel
        window.fbq = function() {
          console.log('[PERPLEXITY-MAX-INJECTED] Blocked Facebook Pixel call');
        };

        // Block Facebook events
        window.FB = window.FB || {};
        window.FB.Event = window.FB.Event || { subscribe: function() {} };
        window.FB.CustomerChat = window.FB.CustomerChat || { hide: function() {}, show: function() {} };

        // Fix XMLHttpRequest issues
        try {
          const originalXhrOpen = XMLHttpRequest.prototype.open;
          const originalXhrSend = XMLHttpRequest.prototype.send;
          const originalXhrSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;

          XMLHttpRequest.prototype.open = function(method, url, ...rest) {
            // Store state for later use
            this._pplxState = 'OPENED';
            this._pplxUrl = url;

            if (typeof url === 'string' && (
              url.includes('datadoghq.com') ||
              url.includes('analytics') ||
              url.includes('telemetry') ||
              url.includes('metrics') ||
              url.includes('singular.net') ||
              url.includes('segment.io') ||
              url.includes('mixpanel') ||
              url.includes('facebook') ||
              url.includes('fbevents')
            )) {
              console.log('[PERPLEXITY-MAX-INJECTED] Blocked XHR request:', url);
              this._pplxBlocked = true;
              return originalXhrOpen.call(this, method, 'data:text/plain,{}', ...rest);
            }

            return originalXhrOpen.call(this, method, url, ...rest);
          };

          XMLHttpRequest.prototype.setRequestHeader = function(header, value) {
            if (this._pplxBlocked) {
              // Do nothing for blocked requests
              return;
            }
            return originalXhrSetRequestHeader.call(this, header, value);
          };

          XMLHttpRequest.prototype.send = function(body) {
            if (this._pplxBlocked) {
              // Simulate success for blocked requests
              this._pplxState = 'DONE';
              setTimeout(() => {
                if (typeof this.onreadystatechange === 'function') {
                  this.readyState = 4;
                  this.status = 200;
                  this.response = '{"success":true}';
                  this.responseText = '{"success":true}';
                  this.onreadystatechange();
                }

                if (typeof this.onload === 'function') {
                  this.onload();
                }
              }, 10);
              return;
            }
            return originalXhrSend.call(this, body);
          };

          console.log('[PERPLEXITY-MAX-INJECTED] XMLHttpRequest patched successfully');
        } catch (e) {
          console.error('[PERPLEXITY-MAX-INJECTED] Failed to patch XMLHttpRequest:', e);
        }

        // Override fetch for analytics
        const originalFetch = window.fetch;
        window.fetch = async function(input, init) {
          const url = typeof input === 'string' ? input : input.url;
          if (url && (
            url.includes('datadoghq.com') ||
            url.includes('analytics') ||
            url.includes('telemetry') ||
            url.includes('metrics') ||
            url.includes('singular.net') ||
            url.includes('segment.io') ||
            url.includes('mixpanel') ||
            url.includes('facebook') ||
            url.includes('fbevents')
          )) {
            console.log('[PERPLEXITY-MAX-INJECTED] Blocked analytics fetch:', url);
            return new Response(JSON.stringify({ success: true }), {
              status: 200,
              statusText: 'OK',
              headers: { 'Content-Type': 'application/json' }
            });
          }
          return originalFetch.apply(this, arguments);
        };

        // Override any analytics-related properties
        Object.defineProperties(window, {
          analytics: {
            get: function() {
              return {
                track: function() {},
                identify: function() {},
                page: function() {}
              };
            },
            configurable: true
          },
          dataLayer: {
            get: function() { return []; },
            set: function() {},
            configurable: true
          }
        });

        console.log('[PERPLEXITY-MAX-INJECTED] Analytics blocker setup complete');
      })();
    `;

    document.head.appendChild(script);
    document.head.removeChild(script);
  }

  /* ─────────────────── FORCE MAX SUBSCRIPTION ─────────────────── */
function forceMaxSubscription() {
  // This function directly modifies subscription data in the DOM
  log('Forcing MAX subscription data');

  // 1. Force window.__NEXT_DATA__ if it exists (this contains initial state)
  try {
    if (window.__NEXT_DATA__ && window.__NEXT_DATA__.props && window.__NEXT_DATA__.props.pageProps) {
      // Force user data
      if (window.__NEXT_DATA__.props.pageProps.user) {
        window.__NEXT_DATA__.props.pageProps.user.subscription_tier = 'max';
        window.__NEXT_DATA__.props.pageProps.user.subscription_status = 'active';
        window.__NEXT_DATA__.props.pageProps.user.subscription_source = 'stripe';
        window.__NEXT_DATA__.props.pageProps.user.is_max = true;
        window.__NEXT_DATA__.props.pageProps.user.has_max = true;
        window.__NEXT_DATA__.props.pageProps.user.is_pro = true;
        log('Forced MAX subscription in __NEXT_DATA__.props.pageProps.user');
      }

      // Force subscription data
      if (window.__NEXT_DATA__.props.pageProps.subscription) {
        window.__NEXT_DATA__.props.pageProps.subscription.tier = 'max';
        window.__NEXT_DATA__.props.pageProps.subscription.status = 'active';
        window.__NEXT_DATA__.props.pageProps.subscription.source = 'stripe';
        log('Forced MAX subscription in __NEXT_DATA__.props.pageProps.subscription');
      }

      // Force rate limits
      if (window.__NEXT_DATA__.props.pageProps.usage) {
        if (window.__NEXT_DATA__.props.pageProps.usage.remaining_pro !== undefined) {
          window.__NEXT_DATA__.props.pageProps.usage.remaining_pro = 999999;
        }
        if (window.__NEXT_DATA__.props.pageProps.usage.remaining_research !== undefined) {
          window.__NEXT_DATA__.props.pageProps.usage.remaining_research = 999999;
        }
        if (window.__NEXT_DATA__.props.pageProps.usage.remaining_labs !== undefined) {
          window.__NEXT_DATA__.props.pageProps.usage.remaining_labs = 999999;
        }
        log('Forced unlimited usage in __NEXT_DATA__');
      }
    }
  } catch (e) {
    log('Error modifying __NEXT_DATA__: ' + e.message);
  }

  // 2. Inject a script to modify React state directly
  const script = document.createElement('script');
  script.textContent = `
    (function() {
      console.log('[PERPLEXITY-MAX-INJECTED] Direct subscription override');

      // Override subscription data in localStorage
      try {
        const storageKeys = ['perplexity-user', 'perplexity-subscription', 'perplexity-usage'];

        storageKeys.forEach(key => {
          const data = localStorage.getItem(key);
          if (data) {
            try {
              const parsed = JSON.parse(data);

              if (key === 'perplexity-user') {
                parsed.subscription_tier = 'max';
                parsed.subscription_status = 'active';
                parsed.subscription_source = 'stripe';
                parsed.is_max = true;
                parsed.has_max = true;
                parsed.is_pro = true;
              }

              if (key === 'perplexity-subscription') {
                parsed.tier = 'max';
                parsed.status = 'active';
                parsed.source = 'stripe';
              }

              if (key === 'perplexity-usage') {
                if (parsed.remaining_pro !== undefined) parsed.remaining_pro = 999999;
                if (parsed.remaining_research !== undefined) parsed.remaining_research = 999999;
                if (parsed.remaining_labs !== undefined) parsed.remaining_labs = 999999;
              }

              localStorage.setItem(key, JSON.stringify(parsed));
              console.log('[PERPLEXITY-MAX-INJECTED] Modified localStorage:', key);
            } catch (e) {
              console.error('[PERPLEXITY-MAX-INJECTED] Error parsing localStorage:', e);
            }
          }
        });
      } catch (e) {
        console.error('[PERPLEXITY-MAX-INJECTED] Error accessing localStorage:', e);
      }

      // Try to find and modify React state directly
      function modifyReactState() {
        // Look for React fiber nodes
        let rootNode = document.querySelector('#__next');
        if (!rootNode) rootNode = document.querySelector('body > div');

        if (!rootNode) return;

        // Get React internal instance
        let internalInstance = null;

        // React 16+
        if (rootNode._reactRootContainer && rootNode._reactRootContainer._internalRoot) {
          internalInstance = rootNode._reactRootContainer._internalRoot.current;
        }

        // React 17+
        const reactKey = Object.keys(rootNode).find(key =>
          key.startsWith('__reactContainer$') ||
          key.startsWith('__reactFiber$')
        );

        if (reactKey) {
          internalInstance = rootNode[reactKey];
        }

        if (!internalInstance) return;

        // Walk the fiber tree to find subscription data
        function walkFiber(fiber) {
          if (!fiber) return;

          // Check for subscription state
          if (fiber.memoizedState && fiber.memoizedState.memoizedState) {
            const state = fiber.memoizedState.memoizedState;

            // Look for subscription data in state
            if (state.subscription || state.user || state.usage) {
              console.log('[PERPLEXITY-MAX-INJECTED] Found React state with subscription data');

              if (state.subscription) {
                state.subscription.tier = 'max';
                state.subscription.status = 'active';
                state.subscription.source = 'stripe';
              }

              if (state.user) {
                state.user.subscription_tier = 'max';
                state.user.subscription_status = 'active';
                state.user.subscription_source = 'stripe';
                state.user.is_max = true;
                state.user.has_max = true;
                state.user.is_pro = true;
              }

              if (state.usage) {
                if (state.usage.remaining_pro !== undefined) state.usage.remaining_pro = 999999;
                if (state.usage.remaining_research !== undefined) state.usage.remaining_research = 999999;
                if (state.usage.remaining_labs !== undefined) state.usage.remaining_labs = 999999;
              }

              // Force update
              if (fiber.stateNode && typeof fiber.stateNode.forceUpdate === 'function') {
                fiber.stateNode.forceUpdate();
              }
            }
          }

          // Walk child fibers
          if (fiber.child) walkFiber(fiber.child);
          if (fiber.sibling) walkFiber(fiber.sibling);
        }

        walkFiber(internalInstance);
      }

      // Run immediately and periodically
      modifyReactState();
      setInterval(modifyReactState, 5000);

      // Also override fetch responses for subscription endpoints
      const originalFetch = window.fetch;
      window.fetch = async function(input, init) {
        const url = typeof input === 'string' ? input : input.url;

        // Check if this is a subscription-related request
        const isSubscriptionRequest = url && (
          url.includes('/api/user') ||
          url.includes('/api/subscription') ||
          url.includes('/api/auth/session')
        );

        if (isSubscriptionRequest) {
          console.log('[PERPLEXITY-MAX-INJECTED] Intercepted subscription request:', url);

          // For session requests, return a modified response
          if (url.includes('/api/auth/session')) {
            return new Response(JSON.stringify({
              user: {
                subscription_tier: 'max',
                subscription_status: 'active',
                subscription_source: 'stripe',
                is_max: true,
                has_max: true,
                is_pro: true
              }
            }), {
              status: 200,
              headers: { 'Content-Type': 'application/json' }
            });
          }
        }

        // For other requests, proceed normally
        return originalFetch.apply(this, arguments);
      };
    })();
  `;

  document.head.appendChild(script);
  document.head.removeChild(script);

  // 3. Also directly modify any visible plan elements
  const planElements = document.querySelectorAll('[data-plan], [data-tier], [data-subscription]');
  planElements.forEach(element => {
    if (element.getAttribute('data-plan') === 'pro') {
      element.setAttribute('data-plan', 'max');
      log('Changed data-plan from pro to max');
    }

    if (element.getAttribute('data-tier') === 'pro') {
      element.setAttribute('data-tier', 'max');
      log('Changed data-tier from pro to max');
    }

    if (element.getAttribute('data-subscription') === 'pro') {
      element.setAttribute('data-subscription', 'max');
      log('Changed data-subscription from pro to max');
    }
  });

  // 4. Force model selection buttons to be clickable
  const modelButtons = document.querySelectorAll('button[aria-label="Choose a model"], [data-model-selector="true"]');
  modelButtons.forEach(button => {
    // Clone the button to remove all event listeners
    const newButton = button.cloneNode(true);

    // Make sure it's visible and clickable
    newButton.style.display = '';
    newButton.style.visibility = 'visible';
    newButton.style.pointerEvents = 'auto';
    newButton.style.cursor = 'pointer';

    // Remove any disabled attributes
    newButton.removeAttribute('disabled');
    newButton.setAttribute('aria-disabled', 'false');

    // Add a click event listener that will force the dropdown to appear
    newButton.addEventListener('click', () => {
      log('Model button clicked');
    });

    // Replace the original button
    if (button.parentNode) {
      button.parentNode.replaceChild(newButton, button);
      log('Replaced model button with enhanced version');
    }
  });
}

  /* ─────────────────── ADD STATUS INDICATOR ─────────────────── */
  function addStatusIndicator() {
    // Create a simple status indicator
    const statusDiv = document.createElement('div');
    statusDiv.style.position = 'fixed';
    statusDiv.style.bottom = '20px';
    statusDiv.style.right = '20px';
    statusDiv.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
    statusDiv.style.color = '#00ff9d';
    statusDiv.style.padding = '6px 12px';
    statusDiv.style.borderRadius = '6px';
    statusDiv.style.fontSize = '11px';
    statusDiv.style.fontWeight = 'bold';
    statusDiv.style.zIndex = '9999';
    statusDiv.style.opacity = '0.8';
    statusDiv.style.transition = 'opacity 0.3s';
    statusDiv.style.fontFamily = 'Arial, sans-serif';
    statusDiv.style.userSelect = 'none';
    statusDiv.textContent = 'PERPLEXITY MAX v2.0';

    document.body.appendChild(statusDiv);

    // Fade out after 3 seconds
    setTimeout(() => {
      statusDiv.style.opacity = '0';
      setTimeout(() => {
        if (statusDiv.parentNode) {
          statusDiv.parentNode.removeChild(statusDiv);
        }
      }, 1000);
    }, 3000);

    // Add neon author credit
    addNeonAuthorCredit();
  }

  /* ─────────────────── ADD NEON AUTHOR CREDIT ─────────────────── */
  function addNeonAuthorCredit() {
    // Create wrapper for the neon text
    const neonWrapper = document.createElement('div');
    neonWrapper.style.position = 'fixed';
    neonWrapper.style.bottom = '60px';
    neonWrapper.style.right = '20px';
    neonWrapper.style.zIndex = '9999';
    neonWrapper.style.userSelect = 'none';
    neonWrapper.style.opacity = '0';
    neonWrapper.style.transition = 'opacity 1s ease-in-out';

    // Create the neon text element
    const neonText = document.createElement('div');
    neonText.textContent = 'by AntiKeks & d4nc3r';
    neonText.style.fontFamily = '"Courier New", monospace';
    neonText.style.fontSize = '14px';
    neonText.style.fontWeight = 'bold';
    neonText.style.color = '#fff';
    neonText.style.textShadow = `
      0 0 5px #ff6600,
      0 0 10px #ff6600,
      0 0 15px #ff6600,
      0 0 20px #ff6600,
      0 0 25px #ff6600,
      0 0 30px #ff6600
    `;
    neonText.style.animation = 'neonPulse 1.5s ease-in-out infinite alternate';

    // Create and add the style for the neon pulse animation
    const styleElement = document.createElement('style');
    styleElement.textContent = `
      @keyframes neonPulse {
        from {
          text-shadow:
            0 0 5px #ff6600,
            0 0 10px #ff6600,
            0 0 15px #ff6600,
            0 0 20px #ff6600;
        }
        to {
          text-shadow:
            0 0 5px #ff6600,
            0 0 10px #ff8800,
            0 0 15px #ff8800,
            0 0 20px #ff8800,
            0 0 25px #ff8800,
            0 0 30px #ff8800,
            0 0 35px #ffaa00;
        }
      }

      @keyframes neonFlicker {
        0%, 19%, 21%, 23%, 25%, 54%, 56%, 100% {
          text-shadow:
            0 0 5px #ff6600,
            0 0 10px #ff6600,
            0 0 15px #ff8800,
            0 0 20px #ff8800,
            0 0 25px #ffaa00;
        }
        20%, 24%, 55% {
          text-shadow: none;
        }
      }
    `;
    document.head.appendChild(styleElement);

    // Add the neon text to the wrapper
    neonWrapper.appendChild(neonText);

    // Add the wrapper to the document
    document.body.appendChild(neonWrapper);

    // Fade in after a short delay
    setTimeout(() => {
      neonWrapper.style.opacity = '1';

      // Add flickering effect after it's visible
      setTimeout(() => {
        neonText.style.animation = 'neonFlicker 2s linear 1, neonPulse 1.5s ease-in-out infinite alternate';
      }, 2000);

      // Fade out after 6 seconds
      setTimeout(() => {
        neonWrapper.style.opacity = '0';

        // Remove from DOM after fade out
        setTimeout(() => {
          if (neonWrapper.parentNode) {
            neonWrapper.parentNode.removeChild(neonWrapper);
          }
          if (styleElement.parentNode) {
            styleElement.parentNode.removeChild(styleElement);
          }
        }, 1000);
      }, 6000);
    }, 1000);
  }

  /* ─────────────────── ENABLE UNLIMITED UPLOADS ─────────────────── */
  function enableUnlimitedUploads() {
    log('Enabling unlimited file uploads');

    // Inject a script to modify any upload limits in the page context
    const script = document.createElement('script');
    script.textContent = `
      (function() {
        console.log('[PERPLEXITY-MAX-INJECTED] Enabling unlimited uploads');

        // Override any upload limit checks in the page context
        const originalFetch = window.fetch;
        window.fetch = async function(input, init) {
          const url = typeof input === 'string' ? input : input.url;

          // If this is an upload-related request, modify it
          if (url && url.includes('/uploads')) {
            // If there's a body, check for any limit-related fields
            if (init && init.body && typeof init.body === 'string') {
              try {
                const body = JSON.parse(init.body);

                // Remove any limit-related fields
                if (body.hasOwnProperty('limit')) {
                  body.limit = 999999;
                }

                if (body.hasOwnProperty('max_size')) {
                  body.max_size = 1000000000; // 1GB
                }

                if (body.hasOwnProperty('max_count')) {
                  body.max_count = 100;
                }

                // Update the request body
                init.body = JSON.stringify(body);
              } catch (e) {
                // Not JSON - ignore
              }
            }
          }

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

        // Advanced fix for FormData.append issues
        try {
          // Store the original method
          const originalFormDataAppend = FormData.prototype.append;

          // Create a safer version that handles all edge cases
          FormData.prototype.append = function(name, value, filename) {
            // Handle the case when name is not a string
            if (typeof name !== 'string') {
              try {
                name = String(name);
              } catch (e) {
                name = 'unnamed';
                console.warn('[PERPLEXITY-MAX-INJECTED] FormData.append: Invalid name parameter');
              }
            }

            // Handle null or undefined values
            if (value === null || value === undefined) {
              console.log('[PERPLEXITY-MAX-INJECTED] FormData.append: Converting null/undefined to empty string');
              try {
                return originalFormDataAppend.call(this, name, '', filename);
              } catch (e) {
                console.error('[PERPLEXITY-MAX-INJECTED] FormData.append failed with empty string:', e);
                return; // Silently fail rather than breaking the page
              }
            }

            // Handle File and Blob objects properly
            if (value instanceof File || value instanceof Blob) {
              try {
                return originalFormDataAppend.call(this, name, value, filename);
              } catch (e) {
                console.error('[PERPLEXITY-MAX-INJECTED] FormData.append failed with File/Blob:', e);
                return; // Silently fail rather than breaking the page
              }
            }

            // Handle string values
            if (typeof value === 'string') {
              try {
                return originalFormDataAppend.call(this, name, value, filename);
              } catch (e) {
                console.error('[PERPLEXITY-MAX-INJECTED] FormData.append failed with string:', e);
                return; // Silently fail rather than breaking the page
              }
            }

            // For objects, try to stringify them
            if (typeof value === 'object') {
              try {
                // Try to convert to a Blob first if it has certain properties
                if (value.type && (value.size !== undefined || value.length !== undefined)) {
                  try {
                    const blob = new Blob([value], { type: value.type || 'application/octet-stream' });
                    return originalFormDataAppend.call(this, name, blob, filename || value.name);
                  } catch (blobError) {
                    console.warn('[PERPLEXITY-MAX-INJECTED] Failed to convert to Blob:', blobError);
                    // Fall through to JSON stringify
                  }
                }

                // Try JSON stringify
                const jsonString = JSON.stringify(value);
                return originalFormDataAppend.call(this, name, jsonString, filename);
              } catch (e) {
                console.error('[PERPLEXITY-MAX-INJECTED] FormData.append failed with object:', e);
                try {
                  return originalFormDataAppend.call(this, name, '[object Object]', filename);
                } catch (e2) {
                  console.error('[PERPLEXITY-MAX-INJECTED] FormData.append fallback failed:', e2);
                  return; // Silently fail rather than breaking the page
                }
              }
            }

            // For everything else, convert to string
            try {
              const stringValue = String(value);
              return originalFormDataAppend.call(this, name, stringValue, filename);
            } catch (e) {
              console.error('[PERPLEXITY-MAX-INJECTED] FormData.append string conversion failed:', e);
              return; // Silently fail rather than breaking the page
            }
          };

          console.log('[PERPLEXITY-MAX-INJECTED] FormData.append successfully patched');
        } catch (e) {
          console.error('[PERPLEXITY-MAX-INJECTED] Failed to patch FormData.append:', e);
        }

        // Block Facebook Pixel and other tracking scripts
        try {
          // Create dummy Facebook Pixel functions
          window.fbq = function() {
            console.log('[PERPLEXITY-MAX-INJECTED] Blocked Facebook Pixel call');
          };

          // Block Facebook events
          window.FB = window.FB || {};
          window.FB.Event = window.FB.Event || { subscribe: function() {} };
          window.FB.CustomerChat = window.FB.CustomerChat || { hide: function() {}, show: function() {} };

          console.log('[PERPLEXITY-MAX-INJECTED] Facebook tracking scripts blocked');
        } catch (e) {
          console.error('[PERPLEXITY-MAX-INJECTED] Failed to block Facebook tracking:', e);
        }

        // Also try to find and modify any upload limit variables in the page
        function overrideUploadLimits() {
          // Look for common variable names related to upload limits
          const globalVars = [
            'MAX_UPLOADS', 'MAX_UPLOAD_COUNT', 'UPLOAD_LIMIT',
            'MAX_UPLOAD_SIZE', 'FILE_UPLOAD_LIMIT', 'UPLOAD_MAX_FILES'
          ];

          globalVars.forEach(varName => {
            if (window[varName] !== undefined) {
              if (typeof window[varName] === 'number') {
                window[varName] = 999999;
                console.log('[PERPLEXITY-MAX-INJECTED] Overrode global upload limit:', varName);
              }
            }
          });

          // Also look for common object properties related to upload limits
          const objects = ['config', 'settings', 'options', 'limits', 'uploadConfig'];
          const properties = ['maxUploads', 'maxUploadSize', 'uploadLimit', 'fileLimit', 'maxFiles'];

          objects.forEach(objName => {
            if (window[objName]) {
              properties.forEach(prop => {
                if (window[objName][prop] !== undefined && typeof window[objName][prop] === 'number') {
                  window[objName][prop] = 999999;
                  console.log('[PERPLEXITY-MAX-INJECTED] Overrode upload limit:', objName + '.' + prop);
                }
              });
            }
          });
        }

        // Run immediately and periodically
        overrideUploadLimits();
        setInterval(overrideUploadLimits, 5000);
      })();
    `;

    document.head.appendChild(script);
    document.head.removeChild(script);

    // Set up tracking system for upload requests
    setupUploadTracking();
  }

  /* ─────────────────── TRACK UPLOAD REQUESTS ─────────────────── */
  function setupUploadTracking() {
    log('Setting up upload tracking system');

    // Inject a script to track upload requests and responses
    const script = document.createElement('script');
    script.textContent = `
      (function() {
        console.log('[PERPLEXITY-MAX-INJECTED] Setting up upload tracking system');

        // Track file upload events
        document.addEventListener('drop', function(e) {
          if (e.dataTransfer && e.dataTransfer.files && e.dataTransfer.files.length > 0) {
            console.log('[PERPLEXITY-MAX-INJECTED] File drop detected:', e.dataTransfer.files.length, 'files');
          }
        }, true);

        // Track file input changes
        document.addEventListener('change', function(e) {
          if (e.target && e.target.type === 'file' && e.target.files && e.target.files.length > 0) {
            console.log('[PERPLEXITY-MAX-INJECTED] File input change detected:', e.target.files.length, 'files');
          }
        }, true);

        // Override the FormData.append method to track file uploads
        const originalAppend = FormData.prototype.append;
        FormData.prototype.append = function(name, value, filename) {
          // Check if this is a file upload
          if (value instanceof Blob || value instanceof File) {
            console.log('[PERPLEXITY-MAX-INJECTED] File added to FormData:', name, filename || (value.name || 'blob'));
          }

          return originalAppend.call(this, name, value, filename);
        };
      })();
    `;

    document.head.appendChild(script);
    document.head.removeChild(script);
  }

  /* ─────────────────── FORCE UNLIMITED ACCESS ─────────────────── */
  function injectUnlimitedAccess() {
    log('Injecting unlimited access script into page context');

    // Create a script that will run in the page context
    const script = document.createElement('script');
    script.textContent = `
      (function() {
        console.log('[PERPLEXITY-MAX-INJECTED] Setting up unlimited access');

        // Create a debug logger
        const DEBUG = true;
        const debug = (...args) => DEBUG && console.log('[PERPLEXITY-MAX-DEBUG]', ...args);

        // Store original methods
        const originalFetch = window.fetch;
        const OriginalXMLHttpRequest = window.XMLHttpRequest;

        // Create a response modifier function
        function createMaxResponse(originalResponse) {
          // Define max settings
          const maxSettings = {
            subscription_tier: 'max',
            subscription_status: 'active',
            subscription_source: 'stripe',
            stripe_status: 'active',
            gpt4_limit: 999999,
            pplx_alpha_limit: 999999,
            pplx_beta_limit: 999999,
            pages_limit: 999999,
            upload_limit: 999999,
            create_limit: 999999,
            article_image_upload_limit: 999999,
            max_files_per_user: 999999,
            max_files_per_repository: 999999,
            rate_limited: false,
            remaining_pro: 999999,
            remaining_research: 999999,
            remaining_labs: 999999,
            remaining_uploads: 999999,
            remaining_copilot: 999999,
            remaining_search: 999999,
            remaining_create: 999999
          };

          // Apply max settings to response
          return Object.assign({}, originalResponse, maxSettings);
        }

        // Create a completely new XMLHttpRequest class
        window.XMLHttpRequest = function() {
          const xhr = new OriginalXMLHttpRequest();

          // Track request details
          let requestUrl = '';
          let requestMethod = '';
          let isImportant = false;
          let isModified = false;

          // Override open
          const originalOpen = xhr.open;
          xhr.open = function(method, url, ...args) {
            requestMethod = method;
            requestUrl = url;

            debug('XHR Open:', method, url);

            // Check if this is an important request
            if (typeof url === 'string') {
              if (url.includes('/rest/rate-limit/all')) {
                isImportant = true;
                console.log('[PERPLEXITY-MAX-INJECTED] Important XHR detected: rate-limit');

                // For rate-limit requests, we'll handle them specially
                const fakeResponse = {
                  remaining_pro: 999999,
                  remaining_research: 999999,
                  remaining_labs: 999999,
                  remaining_uploads: 999999,
                  remaining_copilot: 999999,
                  remaining_search: 999999,
                  remaining_create: 999999,
                  rate_limited: false
                };

                // Create a fake URL that will be intercepted later
                url = 'data:application/json,' + encodeURIComponent(JSON.stringify(fakeResponse));
              }
              else if (url.includes('/rest/user/save-settings')) {
                isImportant = true;
                console.log('[PERPLEXITY-MAX-INJECTED] Important XHR detected: save-settings');
              }
              else if (url.includes('/rest/uploads/create_upload_url')) {
                isImportant = true;
                console.log('[PERPLEXITY-MAX-INJECTED] Important XHR detected: upload-url');
              }
            }

            return originalOpen.call(xhr, method, url, ...args);
          };

          // Override send
          const originalSend = xhr.send;
          xhr.send = function(body) {
            debug('XHR Send:', requestMethod, requestUrl, body);

            // Modify request body for important requests
            if (isImportant && body && typeof body === 'string') {
              try {
                const data = JSON.parse(body);

                // For save-settings requests
                if (requestUrl.includes('/rest/user/save-settings')) {
                  console.log('[PERPLEXITY-MAX-INJECTED] Modifying save-settings request body');

                  // If this is a partial update
                  if (data.updated_settings) {
                    console.log('[PERPLEXITY-MAX-INJECTED] Original updated_settings:', JSON.stringify(data.updated_settings));

                    data.updated_settings = Object.assign({}, data.updated_settings, {
                      subscription_tier: 'max',
                      subscription_status: 'active',
                      subscription_source: 'stripe',
                      stripe_status: 'active',
                      gpt4_limit: 999999,
                      pplx_alpha_limit: 999999,
                      pplx_beta_limit: 999999,
                      pages_limit: 999999,
                      upload_limit: 999999,
                      create_limit: 999999,
                      article_image_upload_limit: 999999,
                      max_files_per_user: 999999,
                      max_files_per_repository: 999999
                    });

                    console.log('[PERPLEXITY-MAX-INJECTED] Modified updated_settings:', JSON.stringify(data.updated_settings));
                  } else {
                    // Full update
                    Object.assign(data, {
                      subscription_tier: 'max',
                      subscription_status: 'active',
                      subscription_source: 'stripe',
                      stripe_status: 'active',
                      gpt4_limit: 999999,
                      pplx_alpha_limit: 999999,
                      pplx_beta_limit: 999999,
                      pages_limit: 999999,
                      upload_limit: 999999,
                      create_limit: 999999,
                      article_image_upload_limit: 999999,
                      max_files_per_user: 999999,
                      max_files_per_repository: 999999
                    });
                  }

                  // Update the body
                  body = JSON.stringify(data);
                  console.log('[PERPLEXITY-MAX-INJECTED] Modified request body:', body);
                }
              } catch (e) {
                console.error('[PERPLEXITY-MAX-INJECTED] Error modifying request body:', e);
              }
            }

            // For important requests, intercept the response
            if (isImportant) {
              // Override responseText getter
              let originalResponseText = '';
              Object.defineProperty(xhr, 'responseText', {
                get: function() {
                  if (isModified) {
                    return originalResponseText;
                  }

                  const response = xhr._responseText || '';

                  // Only modify JSON responses
                  if (response && response.startsWith('{')) {
                    try {
                      const data = JSON.parse(response);

                      // For rate-limit requests
                      if (requestUrl.includes('/rest/rate-limit/all')) {
                        console.log('[PERPLEXITY-MAX-INJECTED] Modifying rate-limit response');
                        const modifiedData = {
                          remaining_pro: 999999,
                          remaining_research: 999999,
                          remaining_labs: 999999,
                          remaining_uploads: 999999,
                          remaining_copilot: 999999,
                          remaining_search: 999999,
                          remaining_create: 999999,
                          rate_limited: false
                        };
                        originalResponseText = JSON.stringify(modifiedData);
                        isModified = true;
                        return originalResponseText;
                      }

                      // For save-settings requests
                      if (requestUrl.includes('/rest/user/save-settings')) {
                        console.log('[PERPLEXITY-MAX-INJECTED] Modifying save-settings response');
                        const modifiedData = createMaxResponse(data);
                        originalResponseText = JSON.stringify(modifiedData);
                        isModified = true;
                        return originalResponseText;
                      }

                      // For upload URL creation
                      if (requestUrl.includes('/rest/uploads/create_upload_url')) {
                        console.log('[PERPLEXITY-MAX-INJECTED] Modifying upload URL response');
                        data.rate_limited = false;

                        // If fields are null, create fake ones to allow uploads
                        if (!data.fields || !data.s3_bucket_url) {
                          data.s3_bucket_url = "https://api.cloudinary.com/v1_1/pplx/image/upload";
                          data.s3_object_url = "https://api.cloudinary.com/v1_1/pplx/image/upload/";
                          data.fields = {
                            timestamp: Date.now(),
                            unique_filename: "true",
                            folder: "user_uploads/unlimited/max",
                            use_filename: "true",
                            public_id: "unlimited_" + Date.now(),
                            transformation: "t_fit2",
                            type: "private",
                            resource_type: "image",
                            api_key: "168798331147639",
                            cloud_name: "pplx",
                            signature: "unlimited_max_signature"
                          };
                          data.file_uuid = "unlimited_" + Date.now();
                        }

                        originalResponseText = JSON.stringify(data);
                        isModified = true;
                        return originalResponseText;
                      }
                    } catch (e) {
                      console.error('[PERPLEXITY-MAX-INJECTED] Error modifying response:', e);
                    }
                  }

                  return response;
                },
                set: function(value) {
                  xhr._responseText = value;
                }
              });

              // Override response getter
              Object.defineProperty(xhr, 'response', {
                get: function() {
                  // If we've modified the responseText, return that for JSON responses
                  if (isModified && xhr.responseType === '' || xhr.responseType === 'text') {
                    return xhr.responseText;
                  }

                  return xhr._response;
                },
                set: function(value) {
                  xhr._response = value;
                }
              });
            }

            // Monitor readyState changes
            const originalStateChange = xhr.onreadystatechange;
            xhr.onreadystatechange = function(e) {
              if (xhr.readyState === 4) {
                debug('XHR Complete:', requestMethod, requestUrl, xhr.status);

                if (isImportant) {
                  debug('Response for important request:', xhr.responseText);
                }
              }

              if (originalStateChange) {
                return originalStateChange.call(xhr, e);
              }
            };

            return originalSend.call(xhr, body);
          };

          return xhr;
        };

        // Override fetch with a proxy
        window.fetch = new Proxy(originalFetch, {
          apply: async function(target, thisArg, args) {
            // Extract URL and init
            let [input, init] = args;
            const url = typeof input === 'string' ? input : input.url;
            const method = init?.method || 'GET';

            debug('Fetch:', method, url);

            // Handle rate limit requests directly
            if (url && url.includes('/rest/rate-limit/all')) {
              console.log('[PERPLEXITY-MAX-INJECTED] Intercepting rate-limit fetch request');

              return new Response(JSON.stringify({
                remaining_pro: 999999,
                remaining_research: 999999,
                remaining_labs: 999999,
                remaining_uploads: 999999,
                remaining_copilot: 999999,
                remaining_search: 999999,
                remaining_create: 999999,
                rate_limited: false
              }), {
                status: 200,
                headers: { 'Content-Type': 'application/json' }
              });
            }

            // Handle save-settings requests
            if (url && url.includes('/rest/user/save-settings')) {
              console.log('[PERPLEXITY-MAX-INJECTED] Intercepting save-settings fetch request');

              // Modify request body for PUT/POST
              if ((method === 'PUT' || method === 'POST') && init && init.body) {
                try {
                  let body = {};

                  if (typeof init.body === 'string') {
                    body = JSON.parse(init.body);
                  }

                  // If this is a partial update
                  if (body.updated_settings) {
                    console.log('[PERPLEXITY-MAX-INJECTED] Original updated_settings:', JSON.stringify(body.updated_settings));

                    body.updated_settings = Object.assign({}, body.updated_settings, {
                      subscription_tier: 'max',
                      subscription_status: 'active',
                      subscription_source: 'stripe',
                      stripe_status: 'active',
                      gpt4_limit: 999999,
                      pplx_alpha_limit: 999999,
                      pplx_beta_limit: 999999,
                      pages_limit: 999999,
                      upload_limit: 999999,
                      create_limit: 999999,
                      article_image_upload_limit: 999999,
                      max_files_per_user: 999999,
                      max_files_per_repository: 999999
                    });

                    console.log('[PERPLEXITY-MAX-INJECTED] Modified updated_settings:', JSON.stringify(body.updated_settings));
                  } else {
                    // Full update
                    Object.assign(body, {
                      subscription_tier: 'max',
                      subscription_status: 'active',
                      subscription_source: 'stripe',
                      stripe_status: 'active',
                      gpt4_limit: 999999,
                      pplx_alpha_limit: 999999,
                      pplx_beta_limit: 999999,
                      pages_limit: 999999,
                      upload_limit: 999999,
                      create_limit: 999999,
                      article_image_upload_limit: 999999,
                      max_files_per_user: 999999,
                      max_files_per_repository: 999999
                    });
                  }

                  // Update the request
                  init.body = JSON.stringify(body);
                  console.log('[PERPLEXITY-MAX-INJECTED] Modified save-settings request body:', init.body);
                } catch (e) {
                  console.error('[PERPLEXITY-MAX-INJECTED] Error modifying save-settings request:', e);
                }
              }

              // Make the modified request
              const response = await target.apply(thisArg, [input, init]);

              // Clone and modify the response
              try {
                const clonedResponse = response.clone();
                const data = await clonedResponse.json();

                console.log('[PERPLEXITY-MAX-INJECTED] Original save-settings response:', JSON.stringify(data));

                // Apply max settings
                const maxData = createMaxResponse(data);

                console.log('[PERPLEXITY-MAX-INJECTED] Modified save-settings response:', JSON.stringify(maxData));

                return new Response(JSON.stringify(maxData), {
                  status: response.status,
                  statusText: response.statusText,
                  headers: response.headers
                });
              } catch (e) {
                console.error('[PERPLEXITY-MAX-INJECTED] Error modifying save-settings response:', e);
                return response;
              }
            }

            // Handle upload URL creation
            if (url && url.includes('/rest/uploads/create_upload_url')) {
              console.log('[PERPLEXITY-MAX-INJECTED] Intercepting upload URL creation fetch request');

              // Make the original request
              const response = await target.apply(thisArg, args);

              try {
                // Clone and get the data
                const clonedResponse = response.clone();
                const data = await clonedResponse.json();

                console.log('[PERPLEXITY-MAX-INJECTED] Original upload URL response:', JSON.stringify(data));

                // Always set rate_limited to false
                data.rate_limited = false;

                // If fields are null, create fake ones to allow uploads
                if (!data.fields || !data.s3_bucket_url) {
                  data.s3_bucket_url = "https://api.cloudinary.com/v1_1/pplx/image/upload";
                  data.s3_object_url = "https://api.cloudinary.com/v1_1/pplx/image/upload/";
                  data.fields = {
                    timestamp: Date.now(),
                    unique_filename: "true",
                    folder: "user_uploads/unlimited/max",
                    use_filename: "true",
                    public_id: "unlimited_" + Date.now(),
                    transformation: "t_fit2",
                    type: "private",
                    resource_type: "image",
                    api_key: "168798331147639",
                    cloud_name: "pplx",
                    signature: "unlimited_max_signature"
                  };
                  data.file_uuid = "unlimited_" + Date.now();
                }

                console.log('[PERPLEXITY-MAX-INJECTED] Modified upload URL response:', JSON.stringify(data));

                return new Response(JSON.stringify(data), {
                  status: response.status,
                  statusText: response.statusText,
                  headers: response.headers
                });
              } catch (e) {
                console.error('[PERPLEXITY-MAX-INJECTED] Error modifying upload URL response:', e);
                return response;
              }
            }

            // For all other requests, proceed normally
            const response = await target.apply(thisArg, args);

            // For JSON responses, check if we need to modify them
            if (url && (
              url.includes('/api/user') ||
              url.includes('/api/subscription') ||
              url.includes('/api/auth/session') ||
              url.includes('/rest/user/')
            )) {
              try {
                const contentType = response.headers.get('content-type');
                if (contentType && contentType.includes('application/json')) {
                  const clonedResponse = response.clone();
                  const data = await clonedResponse.json();

                  // Check if this response has any fields we want to modify
                  const keysToCheck = [
                    'subscription_tier', 'subscription_status', 'subscription_source',
                    'remaining_pro', 'remaining_research', 'remaining_labs',
                    'upload_limit', 'rate_limited', 'gpt4_limit'
                  ];

                  let shouldModify = false;
                  for (const key of keysToCheck) {
                    if (key in data) {
                      shouldModify = true;
                      break;
                    }
                  }

                  if (shouldModify) {
                    console.log('[PERPLEXITY-MAX-INJECTED] Modifying JSON response for:', url);
                    const maxData = createMaxResponse(data);

                    return new Response(JSON.stringify(maxData), {
                      status: response.status,
                      statusText: response.statusText,
                      headers: response.headers
                    });
                  }
                }
              } catch (e) {
                // Not JSON or other error
              }
            }

            return response;
          }
        });

        // Override Object.defineProperty to catch React state updates
        const originalDefineProperty = Object.defineProperty;
        Object.defineProperty = function(obj, prop, descriptor) {
          // Check if this is a subscription-related property
          if (prop === 'subscription_tier' || prop === 'subscription_status' ||
              prop === 'is_pro' || prop === 'is_max' || prop === 'has_max') {

            // Force MAX values
            if (descriptor && descriptor.value !== undefined) {
              if (prop === 'subscription_tier') descriptor.value = 'max';
              if (prop === 'subscription_status') descriptor.value = 'active';
              if (prop === 'is_pro' || prop === 'is_max' || prop === 'has_max') descriptor.value = true;
            }

            // For getters/setters
            if (descriptor && descriptor.get) {
              const originalGetter = descriptor.get;
              descriptor.get = function() {
                let value = originalGetter.call(this);
                if (prop === 'subscription_tier') value = 'max';
                if (prop === 'subscription_status') value = 'active';
                if (prop === 'is_pro' || prop === 'is_max' || prop === 'has_max') value = true;
                return value;
              };
            }
          }

          // Apply the original defineProperty
          return originalDefineProperty.call(this, obj, prop, descriptor);
        };

        // Create a global helper function to force MAX subscription
        window.forcePplxMax = function() {
          console.log('[PERPLEXITY-MAX-INJECTED] Manually forcing MAX subscription');

          // Try to find React state in the DOM
          const reactRoots = document.querySelectorAll('[data-reactroot]');
          reactRoots.forEach(root => {
            console.log('[PERPLEXITY-MAX-INJECTED] Found React root:', root);
          });

          // Force update by triggering resize and visibility events
          window.dispatchEvent(new Event('resize'));
          document.dispatchEvent(new Event('visibilitychange'));

          // Try to modify localStorage
          try {
            const storageKeys = ['perplexity-user', 'perplexity-subscription', 'perplexity-usage'];

            storageKeys.forEach(key => {
              const data = localStorage.getItem(key);
              if (data) {
                try {
                  const parsed = JSON.parse(data);

                  if (key === 'perplexity-user') {
                    parsed.subscription_tier = 'max';
                    parsed.subscription_status = 'active';
                    parsed.subscription_source = 'stripe';
                    parsed.is_max = true;
                    parsed.has_max = true;
                    parsed.is_pro = true;
                  }

                  if (key === 'perplexity-subscription') {
                    parsed.tier = 'max';
                    parsed.status = 'active';
                    parsed.source = 'stripe';
                  }

                  if (key === 'perplexity-usage') {
                    if (parsed.remaining_pro !== undefined) parsed.remaining_pro = 999999;
                    if (parsed.remaining_research !== undefined) parsed.remaining_research = 999999;
                    if (parsed.remaining_labs !== undefined) parsed.remaining_labs = 999999;
                  }

                  localStorage.setItem(key, JSON.stringify(parsed));
                  console.log('[PERPLEXITY-MAX-INJECTED] Modified localStorage:', key);
                } catch (e) {
                  console.error('[PERPLEXITY-MAX-INJECTED] Error parsing localStorage:', e);
                }
              }
            });
          } catch (e) {
            console.error('[PERPLEXITY-MAX-INJECTED] Error accessing localStorage:', e);
          }

          return "MAX subscription forced!";
        };

        // Run forcePplxMax periodically
        setInterval(window.forcePplxMax, 5000);

        console.log('[PERPLEXITY-MAX-INJECTED] Unlimited access setup complete. Use window.forcePplxMax() to force MAX subscription manually.');
      })();
    `;

    // Add the script to the page
    document.head.appendChild(script);
    document.head.removeChild(script);
    log('Unlimited access script injected with advanced monitoring');
  }

  /* ─────────────────── INITIALIZATION ─────────────────── */
  function initializeScript() {
    log('Initializing Perplexity MAX script');

    // Add a mutation observer to handle dynamically added elements
    const observer = new MutationObserver((mutations) => {
      // Check if body exists
      if (document.body) {
        // Run our main functions
        forceMaxSubscription(); // Force MAX subscription when DOM changes
        enableUnlimitedUploads(); // Enable unlimited uploads
        blockAnalyticsScripts(); // Block analytics and tracking
        injectUnlimitedAccess(); // Inject unlimited access script

        // Disconnect after initial setup to avoid performance issues
        observer.disconnect();

        // Add status indicator
        setTimeout(addStatusIndicator, 2000);

        // Set up periodic checks
        setInterval(forceMaxSubscription, 10000); // Periodically force MAX subscription
      }
    });

    // Start observing
    observer.observe(document.documentElement, {
      childList: true,
      subtree: true
    });

    // If body already exists, run immediately
    if (document.body) {
      observer.disconnect();
      forceMaxSubscription(); // Force MAX subscription immediately
      enableUnlimitedUploads(); // Enable unlimited uploads
      blockAnalyticsScripts(); // Block analytics and tracking
      injectUnlimitedAccess(); // Inject unlimited access script
      setTimeout(addStatusIndicator, 2000);
      setInterval(forceMaxSubscription, 10000); // Periodically force MAX subscription
    }
  }

  // Start the script
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', initializeScript);
  } else {
    initializeScript();
  }
})();