Enhance Perplexity AI chat

Increase the width of the conversation panel and replaces MCP button with MCP SuperAssistant extension

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

You will need to install an extension such as Tampermonkey to install this script.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name Enhance Perplexity AI chat
// @namespace facelook.hk
// @version 1.5
// @author FacelookHK
// @description Increase the width of the conversation panel and replaces MCP button with MCP SuperAssistant extension
// @license none
// @match https://www.perplexity.ai/*
// @grant none
// ==/UserScript==

(function () {
    "use strict";

    const OVERLAY_ID = "mcp-custom-overlay-btn";

    // Standard Blue Fallback (Perplexity "Super" Blue)
    const DEFAULT_STYLE = {
        width: "32px", // Standard small button size
        height: "32px",
        bg: "var(--super, #3C82F6)",
        radius: "8px" // Standard radius
    };

    // Simple Cube/Plugin Icon
    const SVG_ICON = `
    <svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
        <path d="M21 16V8.00002C20.9996 7.64927 20.9071 7.30481 20.7315 7.00116C20.556 6.69751 20.3037 6.44536 20 6.27002L13 2.27002C12.696 2.09449 12.3511 2.00205 12 2.00205C11.6489 2.00205 11.304 2.09449 11 2.27002L4 6.27002C3.69626 6.44536 3.44398 6.69751 3.26846 7.00116C3.09294 7.30481 3.00036 7.64927 3 8.00002V16C3.00036 16.3508 3.09294 16.6952 3.26846 16.9989C3.44398 17.3025 3.69626 17.5547 4 17.73L11 21.73C11.304 21.9056 11.6489 21.998 12 21.998C12.3511 21.998 12.696 21.9056 13 21.73L20 17.73C20.3037 17.5547 20.556 17.3025 20.7315 16.9989C20.9071 16.6952 20.9996 16.3508 21 16Z" stroke="#606260" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
    </svg>`;

    // Selectors to find the target voice button
    const voiceBtnSelectors = [
        'button[aria-label="Voice Search"]',
        'button[aria-label="Voice Mode"]',
        '[data-testid="voice-search-button"]',
        '.fa-microphone' // Fallback icon search
    ];

    // Layout Selectors (Existing)
    const layoutSelectors = [
        { sel: ".mx-auto.max-w-threadContentWidth", prop: "max-width", val: (artifact) => artifact ? "85%" : "65%" },
        { sel: ".md\\:max-w-threadContentWidth.md\\:mx-auto.md\\:w-full", prop: "margin-left", val: "unset" },
        { sel: ".max-w-threadContentWidth.relative.isolate", prop: "max-width", val: "unset" },
        { sel: ".max-w-\\[300px\\]", prop: "max-width", val: "90%" },
        { sel: ".isolate.mx-auto.max-w-threadContentWidth", prop: "max-width", val: "unset" }
    ];

    function getVoiceButtonStyle() {
        for (const sel of voiceBtnSelectors) {
            const el = document.querySelector(sel);
            // Need to find the actual button container, sometimes the selector hits the icon
            const btn = el?.tagName === 'BUTTON' ? el : el?.closest('button');

            if (btn) {
                const style = window.getComputedStyle(btn);
                // Ensure we are getting valid visible dimensions
                if (parseFloat(style.width) > 0) {
                    return {
                        width: style.width,
                        height: style.height,
                        bg: style.backgroundColor,
                        radius: style.borderRadius,
                        padding: style.padding
                    };
                }
            }
        }
        return null; // Not found yet
    }

    function apply() {
        const artifactExists = document.getElementById("cplx-artifact");
        layoutSelectors.forEach(item => {
            document.querySelectorAll(item.sel).forEach(el => {
                const value = typeof item.val === "function" ? item.val(artifactExists) : item.val;
                el.style.setProperty(item.prop, value, "important");
            });
        });

        // Get target style dynamically
        const targetStyle = getVoiceButtonStyle() || DEFAULT_STYLE;

        const buttons = document.querySelectorAll("button.mcp-perplexity-button-base");
        buttons.forEach(originalBtn => {

            // Hide Original
            if (originalBtn.style.opacity !== "0") {
                originalBtn.style.cssText = `
                    opacity: 0 !important;
                    visibility: visible !important;
                    width: ${targetStyle.width} !important;
                    height: ${targetStyle.height} !important;
                    min-width: ${targetStyle.width} !important;
                    padding: 0 !important;
                    margin: 0 !important;
                    position: absolute !important;
                    top: 0; left: 0; z-index: 0 !important;
                `;
            }

            const parent = originalBtn.parentElement;
            if (parent) {
                parent.style.position = "relative";
                parent.style.display = "inline-flex";
                parent.style.width = targetStyle.width;
                parent.style.height = targetStyle.height;
                parent.style.alignItems = "center";
                parent.style.justifyContent = "center";
            }

            let overlay = parent.querySelector(`#${OVERLAY_ID}`);
            if (!overlay) {
                overlay = document.createElement("div");
                overlay.id = OVERLAY_ID;
                overlay.innerHTML = SVG_ICON;

                overlay.addEventListener("click", (e) => {
                    e.preventDefault();
                    e.stopPropagation();
                    originalBtn.click();
                });

                parent.appendChild(overlay);
            }

            // Apply Styles to Overlay (Continuously update in case theme changes)
            overlay.style.cssText = `
                position: absolute;
                top: 1px; left: 0;
                width: 36px; height: 36px;
                background-color: #191B1B; //${targetStyle.bg};
                border-radius: ${targetStyle.radius};
                display: flex; justify-content: center; align-items: center;
                cursor: pointer;
                z-index: 10;
                color: white;
                transition: background-color 0.2s;
                pointer-events: auto;
                box-shadow: 0 1px 2px rgba(0,0,0,0.1);
            `;
        });
    }

    if (document.readyState === "loading") {
        document.addEventListener("DOMContentLoaded", apply, { once: true });
    } else {
        apply();
    }
    //setInterval(apply, 500);
    apply()
    const mo = new MutationObserver(apply);
    mo.observe(document.documentElement, { childList: true, subtree: true });
})();