您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Auto-enable Incognito mode, enable Social sources, and open the Model switcher popup on perplexity.ai with a full-screen True Turquoise animated loader
// ==UserScript== // @name Perplexity Helper — True Turquoise // @namespace https://greasyfork.org/users/1513610 // @version 1.0.0 // @description Auto-enable Incognito mode, enable Social sources, and open the Model switcher popup on perplexity.ai with a full-screen True Turquoise animated loader // @description:en Auto-enable Incognito mode, enable Social sources, and open the Model switcher popup on perplexity.ai with a full-screen True Turquoise animated loader // @license MIT // @match https://www.perplexity.ai/* // @run-at document-idle // @grant none // ==/UserScript== (() => { 'use strict'; // ────── guard: avoid double-run ────────────────────────────────────────── if (window.__ppxHelperActive) return; window.__ppxHelperActive = true; // ────── configuration ──────────────────────────────────────────────────── const CFG = { debug: false, initialDelayMs: 1000, waitTimeoutMs: 10_000, waitIntervalMs: 100, smallPauseMs: 300, menuPauseMs: 400, loader: { enable: true }, }; // ────── logging ───────────────────────────────────────────────────────── const log = (...args) => { if (!CFG.debug) return; console.log('%c[Perplexity-Helper]', 'color:#1ABC9C;font-weight:bold;', ...args); }; const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); function withTimeout(promise, ms, label = 'timeout') { let t; const timeout = new Promise((_, rej) => (t = setTimeout(() => rej(new Error(label)), ms))); return Promise.race([promise.finally(() => clearTimeout(t)), timeout]); } function waitFor(predicate, { timeout = CFG.waitTimeoutMs, interval = CFG.waitIntervalMs } = {}) { return withTimeout( new Promise((resolve, reject) => { (function tick() { try { const val = predicate(); if (val) return resolve(val); setTimeout(tick, interval); } catch (e) { reject(e); } })(); }), timeout, 'waitFor: timed-out' ); } function waitForEl(selector, { timeout = CFG.waitTimeoutMs, root = document } = {}) { const now = root.querySelector(selector); if (now) return Promise.resolve(now); if (typeof MutationObserver !== 'function') { return waitFor(() => root.querySelector(selector), { timeout, interval: CFG.waitIntervalMs }); } return withTimeout( new Promise((resolve, reject) => { let done = false; const finish = (val, err) => { if (done) return; done = true; try { observer.disconnect(); } catch {} clearTimeout(timer); if (err) reject(err); else resolve(val); }; const observer = new MutationObserver(() => { try { const el = root.querySelector(selector); if (el) finish(el); } catch (e) { finish(null, e); } }); try { observer.observe(root === document ? document.documentElement : root, { childList: true, subtree: true, }); } catch (e) { // Fallback polling if observe fails (rare) waitFor(() => root.querySelector(selector), { timeout, interval: CFG.waitIntervalMs }) .then(resolve, reject); return; } queueMicrotask(() => { const el = root.querySelector(selector); if (el) finish(el); }); const timer = setTimeout(() => { finish(null, new Error(`waitForEl: timed-out for "${selector}"`)); }, timeout); }), timeout + 50, 'waitForEl: timed-out' ); } const normalize = (s) => s?.replace(/\s+/g, ' ').trim() ?? ''; async function findByText(tag, text, { exact = false, timeout = 3_000 } = {}) { const needle = normalize(text); return waitFor(() => { const nodes = Array.from(document.getElementsByTagName(tag)); return nodes.find((n) => { const t = normalize(n.textContent || ''); return exact ? t === needle : t.toLowerCase().includes(needle.toLowerCase()); }); }, { timeout, interval: CFG.waitIntervalMs }); } async function safeClick(el) { if (!el) return; try { el.scrollIntoView?.({ block: 'center', inline: 'center' }); await sleep(36); el.dispatchEvent?.(new MouseEvent('click', { bubbles: true, cancelable: true, view: window })); el.click?.(); } catch {} } // ────── animated loader (True Turquoise) ───────────────────────────────── function showLoader() { if (!CFG.loader.enable) return; try { document.getElementById('ppx-loader-backdrop')?.remove(); document.getElementById('ppx-loader-style')?.remove(); const style = document.createElement('style'); style.id = 'ppx-loader-style'; style.textContent = ` :root { --ppx-bg: rgba(6, 18, 18, 0.75); --ppx-card: rgba(255, 255, 255, 0.06); --ppx-stroke: rgba(255, 255, 255, 0.16); --ppx-accent: #40E0D0; --ppx-accent-2: #1ABC9C; --ppx-text: #eafffb; --ppx-subtext: #bff3ea; } #ppx-loader-backdrop { position: fixed; inset: 0; z-index: 2147483647; display: grid; place-items: center; background: radial-gradient(1200px 800px at 10% -10%, rgba(64,224,208,0.14), transparent 60%), radial-gradient(1000px 700px at 110% 110%, rgba(26,188,156,0.12), transparent 60%), var(--ppx-bg); backdrop-filter: saturate(120%) blur(5px); -webkit-backdrop-filter: saturate(120%) blur(5px); animation: ppx-fade-in 220ms ease-out both; pointer-events: auto; } #ppx-loader-card { display: grid; grid-auto-rows: min-content; gap: 14px; padding: 24px 28px; border-radius: 16px; background: var(--ppx-card); box-shadow: 0 10px 30px rgba(0,0,0,0.35), inset 0 0 0 1px var(--ppx-stroke); transform: translateZ(0); animation: ppx-pop 220ms cubic-bezier(.2,.9,.2,1) both; } #ppx-spinner-wrap { position: relative; width: 64px; height: 64px; margin-inline: auto; } #ppx-spinner { box-sizing: border-box; width: 64px; height: 64px; border-radius: 50%; border: 6px solid rgba(255,255,255,0.18); border-top-color: var(--ppx-accent); border-right-color: var(--ppx-accent-2); animation: ppx-spin 1000ms linear infinite; } #ppx-spinner-ring { position: absolute; inset: -8px; border-radius: 50%; border: 2px dotted rgba(64,224,208,0.28); animation: ppx-spin 6s linear infinite reverse; } #ppx-title { color: var(--ppx-text); font: 700 15px/1.35 ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, Arial; text-align: center; letter-spacing: .2px; } #ppx-sub { color: var(--ppx-subtext); font: 600 12.5px/1.4 ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, Arial; text-align: center; } #ppx-dots { display: inline-grid; grid-auto-flow: column; gap: 4px; margin-left: 4px; vertical-align: baseline; } .ppx-dot { width: 5px; height: 5px; border-radius: 50%; background: var(--ppx-accent); opacity: .45; animation: ppx-bounce 900ms ease-in-out infinite; } .ppx-dot:nth-child(2) { animation-delay: 120ms; } .ppx-dot:nth-child(3) { animation-delay: 240ms; } @keyframes ppx-spin { to { transform: rotate(360deg); } } @keyframes ppx-bounce { 0%,100% { transform: translateY(0); opacity: .5; } 50% { transform: translateY(-3px); opacity: 1; } } @keyframes ppx-fade-in { from { opacity: 0; } to { opacity: 1; } } @keyframes ppx-pop { from { transform: translateY(4px) scale(.98); opacity: 0; } to { transform: translateY(0) scale(1); opacity: 1; } } @media (prefers-reduced-motion: reduce) { #ppx-spinner, #ppx-spinner-ring, .ppx-dot { animation-duration: 2.5s; } } `; const overlay = document.createElement('div'); overlay.id = 'ppx-loader-backdrop'; overlay.setAttribute('aria-hidden', 'true'); const card = document.createElement('div'); card.id = 'ppx-loader-card'; card.setAttribute('role', 'status'); card.setAttribute('aria-live', 'polite'); const spinnerWrap = document.createElement('div'); spinnerWrap.id = 'ppx-spinner-wrap'; const spinner = document.createElement('div'); spinner.id = 'ppx-spinner'; const ring = document.createElement('div'); ring.id = 'ppx-spinner-ring'; const title = document.createElement('div'); title.id = 'ppx-title'; title.textContent = 'Preparing workspace'; const sub = document.createElement('div'); sub.id = 'ppx-sub'; sub.innerHTML = 'Please wait<span id="ppx-dots"><i class="ppx-dot"></i><i class="ppx-dot"></i><i class="ppx-dot"></i></span>'; spinnerWrap.append(spinner, ring); card.append(spinnerWrap, title, sub); overlay.append(card); const prevOverflow = { html: document.documentElement.style.overflow, body: document.body.style.overflow, }; document.documentElement.style.overflow = 'hidden'; document.body.style.overflow = 'hidden'; const prevBusyHtml = document.documentElement.getAttribute('aria-busy'); document.documentElement.setAttribute('aria-busy', 'true'); document.head.appendChild(style); document.body.appendChild(overlay); window.__ppxAnimLock = { style, overlay, prevOverflow, prevBusyHtml }; } catch (err) { log('showLoader error:', err?.message || err); } } function hideLoader() { if (!CFG.loader.enable) return; try { const lock = window.__ppxAnimLock; if (!lock) return; const { overlay, style, prevOverflow, prevBusyHtml } = lock; overlay.style.animation = 'ppx-fade-in 180ms ease-in reverse both'; setTimeout(() => { try { overlay?.parentNode?.removeChild(overlay); style?.parentNode?.removeChild(style); } catch {} document.documentElement.style.overflow = prevOverflow?.html ?? ''; document.body.style.overflow = prevOverflow?.body ?? ''; if (prevBusyHtml == null) { document.documentElement.removeAttribute('aria-busy'); } else { document.documentElement.setAttribute('aria-busy', prevBusyHtml); } delete window.__ppxAnimLock; }, 180); } catch (err) { log('hideLoader error:', err?.message || err); } } // ────── tasks ──────────────────────────────────────────────────────────── async function toggleIncognitoIfNeeded() { try { const avatarBtn = await waitForEl('[data-testid="sidebar-popover-trigger-signed-in"], [data-testid="sidebar-popover-trigger"]', { timeout: 5_000 }); // Heuristic: if avatar has an <img> it likely indicates signed-in mode; otherwise Incognito is already active. if (!avatarBtn.querySelector('img')) { log('Incognito appears already enabled'); return; } await safeClick(avatarBtn); log('Avatar clicked. Waiting for menu…'); await sleep(CFG.menuPauseMs); // Find the "Incognito" control by common patterns: explicit text or a role=switch near "Incognito". let incognitoBtn = null; try { incognitoBtn = await findByText('button', 'Incognito', { exact: false, timeout: 3_000 }); } catch {} if (!incognitoBtn) { // Fallback: any switch adjacent to a label containing "Incognito" const buttons = Array.from(document.querySelectorAll('button[role="switch"], [role="menuitemcheckbox"], button')); const guess = buttons.find((b) => /incognito/i.test(b.textContent || '') || /incognito/i.test(b.getAttribute('aria-label') || '')); if (guess) incognitoBtn = guess; } await safeClick(incognitoBtn); log('Incognito enabled'); await sleep(CFG.smallPauseMs); } catch (err) { log('toggleIncognitoIfNeeded error:', err?.message || err); } } async function enableSocialSources() { try { const sourcesBtn = await waitForEl('[data-testid="sources-switcher-button"]', { timeout: 6_000 }); await safeClick(sourcesBtn); log('Sources button clicked. Waiting for popup…'); await sleep(CFG.menuPauseMs); const socialToggleContainer = await waitForEl('[data-testid="source-toggle-social"]', { timeout: 6_000 }); const socialSwitch = socialToggleContainer.querySelector('button[role="switch"], [role="switch"]'); const isEnabled = socialSwitch?.getAttribute('aria-checked') === 'true' || socialSwitch?.getAttribute('data-state') === 'checked'; if (!isEnabled) { await safeClick(socialSwitch); log('Social sources enabled'); await sleep(CFG.smallPauseMs); } else { log('Social sources already enabled'); } } catch (err) { log('enableSocialSources error:', err?.message || err); } } async function openModelSwitcher() { try { let modelBtn = document.querySelector('[data-testid="model-switcher-button"]') || document.querySelector('button[aria-haspopup="menu"][aria-expanded], .max-w-24'); modelBtn = modelBtn || (await waitForEl('[data-testid="model-switcher-button"]', { timeout: 5_000 })); await safeClick(modelBtn); log('Model switcher opened (no selection performed)'); await sleep(CFG.smallPauseMs); } catch (err) { log(`openModelSwitcher error: ${err?.message || err}`); } } // ────── bootstrap ──────────────────────────────────────────────────────── (async () => { try { await sleep(CFG.initialDelayMs); showLoader(); await toggleIncognitoIfNeeded(); await enableSocialSources(); await openModelSwitcher(); log('Helper script completed'); } catch (err) { log('bootstrap error:', err?.message || err); } finally { hideLoader(); delete window.__ppxHelperActive; } })(); })();