// ==UserScript==
// @name Perplexity Playground Reimagined
// @namespace http://tampermonkey.net/
// @version 1.0
// @description Restores the appearance of Perplexity Labs Playground on perplexity.ai, including precise layout, colors, and button placements.
// @author YouTubeDrawaria
// @match https://www.perplexity.ai/*
// @icon https://web.archive.org/web/20250804233216im_/https://playground.perplexity.ai/favicon.ico
// @grant GM_addStyle
// @run-at document-start
// @license MIT
// ==/UserScript==
(function() {
'use strict';
// Define color variables para emular el tema oscuro del Playground
GM_addStyle(`
:root {
/* Mimic Playground dark mode colors more closely */
--playground-background-base: #1f1f1d; /* html background-color */
--playground-background-offset: #2D2D2D; /* input background, chat bubble background */
--playground-border-color: rgba(255, 255, 255, 0.1); /* common border color */
--playground-text-foreground: #E0E0E0; /* main text color */
--playground-text-off: #999; /* quieter text, placeholder */
--playground-super-blue: #0A84FF; /* send button color */
--playground-button-bg: #333; /* model selector button background */
--playground-button-border: #555; /* model selector button border */
--playground-scrollbar-thumb: #3D3D3B; /* for scrollbars in dark mode */
--playground-disabled-button-bg: #666; /* disabled button background */
--playground-disabled-button-text: #999; /* disabled button text/icon */
--playground-header-button-bg: #2D2D2D; /* Background for sonar/try perplexity buttons */
--playground-header-button-border: rgba(255, 255, 255, 0.15); /* Border for sonar/try perplexity buttons */
}
/* Fondo global para el tema oscuro */
html.dark, body.dark, .dark main {
background-color: var(--playground-background-base) !important;
}
.md\\:bg-underlay,
.bg-base { /* Fondo principal de la aplicación */
background-color: var(--playground-background-base) !important;
}
/* Asegura que componentes específicos también usen el fondo base si aparecen fuera del contenido central */
.border-subtlest.ring-subtlest.divide-subtlest.bg-offset {
background-color: var(--playground-background-base) !important;
}
/* Oculta completamente la barra lateral izquierda */
.group\\/sidebar {
display: none !important;
}
/* Ajusta el contenedor principal para que ocupe todo el ancho y centre el contenido */
.isolate.flex.h-dvh {
width: 100% !important;
max-width: none !important;
}
.erp-tab\\:p-0.md\\:gap-xs.erp-tab\\:gap-0.lg\\:py-sm.lg\\:pe-sm.isolate.flex.h-auto.max-h-screen.min-w-0.grow.flex-col {
width: 100% !important;
max-width: none !important;
padding-left: 0 !important;
padding-right: 0 !important;
}
/* Ajusta el contenedor de contenido desplazable para que esté centrado y sea ancho */
.mx-auto.size-full.max-w-screen-md.px-md.md\\:px-lg,
.erp-sidecar\\:px-md.sm\\:px-md.md\\:px-lg.isolate.mx-auto.size-full.sm\\:max-w-screen-md {
max-width: 960px !important; /* Más ancho que el predeterminado, similar al contenido principal del Playground */
margin-left: auto !important;
margin-right: auto !important;
padding-left: 16px !important; /* Añade algo de relleno */
padding-right: 16px !important; /* Añade algo de relleno */
}
/* Reutiliza y estiliza el encabezado móvil como el encabezado principal del Playground */
.py-md.h-headerHeight.flex.items-center.justify-between.border-b.md\\:hidden {
display: flex !important; /* Siempre visible */
position: sticky !important;
top: 0 !important;
z-index: 20 !important;
width: 100% !important;
box-sizing: border-box !important;
padding: 12px 24px !important; /* Coincide con el relleno del Playground */
border-bottom: 1px solid var(--playground-border-color) !important;
background-color: var(--playground-background-base) !important; /* Asegura un fondo consistente */
}
.py-md.h-headerHeight.flex.items-center.justify-between.border-b.md\\:hidden > div:first-child { /* Lado izquierdo (logo y texto) */
margin-left: 0 !important;
}
.py-md.h-headerHeight.flex.items-center.justify-between.border-b.md\\:hidden .gap-x-sm.flex.items-center { /* Lado derecho (botones) */
margin-right: 0 !important;
margin-left: auto !important; /* Empuja a la derecha */
gap: 8px !important; /* Espacio entre los botones del header */
}
.py-md.h-headerHeight.flex.items-center.justify-between.border-b.md\\:hidden .font-sans.text-base.text-foreground {
color: var(--playground-text-foreground) !important;
font-size: 14px !important; /* Tamaño de fuente para el texto "Perplexity Playground" */
}
.py-md.h-headerHeight.flex.items-center.justify-between.border-b.md\\:hidden .tabler-icon {
color: var(--playground-text-foreground) !important;
}
/* Estilo para los botones "sonar" y "Try Perplexity" en el encabezado */
.py-md.h-headerHeight.flex.items-center.justify-between.border-b.md\\:hidden .gap-sm.flex.items-center a[role="button"] {
background-color: var(--playground-header-button-bg) !important;
border: 1px solid var(--playground-header-button-border) !important;
border-radius: 8px !important;
height: 32px !important; /* Coincide con la captura */
padding: 0 12px !important; /* Relleno lateral */
font-size: 14px !important;
color: var(--playground-text-foreground) !important;
display: inline-flex !important;
align-items: center !important;
justify-content: center !important;
gap: 6px !important; /* Espacio entre icono y texto */
}
.py-md.h-headerHeight.flex.items-center.justify-between.border-b.md\\:hidden .gap-sm.flex.items-center a[role="button"] svg {
color: var(--playground-text-foreground) !important;
width: 16px !important;
height: 16px !important;
}
/* Estilo específico para el botón "Try Perplexity" (azul) */
.py-md.h-headerHeight.flex.items-center.justify-between.border-b.md\\:hidden .gap-sm.flex.items-center a[href="https://www.perplexity.ai/"] {
background-color: var(--playground-super-blue) !important;
color: white !important;
border-color: var(--playground-super-blue) !important; /* Asegura un borde azul */
}
/* Oculta el encabezado de escritorio predeterminado (el gran logo y texto en el centro) */
.mb-lg.bottom-0.flex.w-full.items-center.justify-center.pb-3.text-center.md\\:absolute {
display: none !important;
}
/* Relleno del área principal de contenido del chat */
.scrollable-container.scrollbar-subtle.flex.flex-1.basis-0.overflow-auto {
padding-top: 20px !important; /* Añade espacio en la parte superior del contenido desplazable */
padding-bottom: 20px !important; /* Añade espacio en la parte inferior */
flex-direction: column !important; /* Apila los elementos normalmente */
justify-content: flex-start !important; /* Comienza desde la parte superior */
}
/* Estilo específico para la burbuja del mensaje de chat inicial */
#playground-initial-message {
background-color: transparent !important;
border: none !important;
padding-top: 0 !important;
padding-bottom: 0 !important;
width: 100%; /* Asegura que ocupe todo el ancho del área de contenido */
box-sizing: border-box;
padding: 16px !important; /* Coincide con el relleno del contenido */
}
#playground-initial-message .max-w-full.text-right {
max-width: fit-content !important; /* Se ajusta al contenido */
text-align: left !important; /* Alinea el texto a la izquierda */
margin-left: 0 !important; /* Elimina cualquier margen automático que lo empuje a la derecha */
}
#playground-initial-message .px-md.py-sm.max-w-full {
background-color: var(--playground-background-offset) !important; /* Coincide con el fondo de la entrada */
border: 1px solid var(--playground-border-color) !important;
color: var(--playground-text-foreground) !important;
border-radius: 8px !important; /* Esquinas redondeadas para la burbuja */
box-shadow: none !important;
max-width: fit-content !important;
padding: 10px 16px !important; /* Relleno interno ajustado para la burbuja */
}
#playground-initial-message .font-sans.text-base.font-medium.text-textOff {
color: var(--playground-text-off) !important; /* Texto más tenue para el mensaje "served by" */
}
#playground-initial-message .prose p {
color: var(--playground-text-foreground) !important; /* Asegura que el texto del mensaje principal sea claro */
margin-top: 4px !important; /* Pequeño margen para el texto debajo de la información del modelo */
margin-bottom: 0 !important;
}
#playground-initial-message button { /* Botón de Copiar para el mensaje inicial */
display: inline-flex !important; /* Hace visible el botón de copiar */
background-color: transparent !important;
border: none !important;
color: var(--playground-text-off) !important;
box-shadow: none !important;
padding: 4px 8px !important;
font-size: 14px !important;
border-radius: 6px !important;
margin-left: 0 !important; /* Ajusta la posición si es necesario */
margin-top: 4px !important; /* Espacio debajo de la burbuja */
}
#playground-initial-message button:hover {
background-color: rgba(255, 255, 255, 0.05) !important;
color: var(--playground-text-foreground) !important;
}
#playground-initial-message button svg {
color: var(--playground-text-off) !important;
}
#playground-initial-message button:hover svg {
color: var(--playground-text-foreground) !important;
}
/* Ajustes para el área de contenido principal */
.flex.h-full.grow > div.grow { /* Contenedor alrededor del área de contenido principal */
width: 100%;
}
.md\\:px-md.mx-auto.flex.h-full.w-full.max-w-screen-lg.grow.flex-col {
max-width: 100% !important; /* Permite que el ancho máximo interno lo controle */
padding-left: 0 !important;
padding-right: 0 !important;
}
/* Contenedor del footer para la caja de entrada */
.sticky.bottom-0.z-20.border-t {
background-color: var(--playground-background-base) !important; /* Coincide con el fondo general */
border-top: 1px solid var(--playground-border-color) !important;
padding: 16px !important; /* Relleno consistente para el área del footer */
box-shadow: none !important;
}
/* Oculta los elementos debajo de la entrada que muestran "0.00 sec" y el selector de modelo original */
.md\\:px-md.mx-auto.max-w-screen-lg > .gap-md.p-md.flex.flex-col.justify-between {
display: none !important; /* Oculta la antigua sección del temporizador/modelo */
}
/* Estilos para toda la barra de entrada */
.bg-raised.w-full.outline-none.flex.items-center.border.rounded-2xl.dark\\:bg-offset {
background-color: var(--playground-background-offset) !important;
border-color: var(--playground-border-color) !important;
border-width: 1px !important;
border-style: solid !important;
border-radius: 12px !important;
box-shadow: none !important;
padding: 8px !important; /* Relleno para toda la barra de entrada combinada */
max-width: 960px !important; /* Coincide con el ancho del contenido */
margin-left: auto !important;
margin-right: auto !important;
display: flex !important; /* Lo convierte en un contenedor flex */
align-items: center !important; /* Alinea los elementos verticalmente */
gap: 8px !important; /* Espacio entre los elementos */
}
/* Elimina el relleno interno del antiguo contenedor de la cuadrícula */
.bg-raised.w-full.outline-none.flex.items-center.border.rounded-2xl.dark\\:bg-offset > .px-3\\.5.grid-rows-1fr-auto.grid.grid-cols-3 {
padding: 0 !important;
display: contents !important; /* Hace que sus hijos sean hijos directos del contenedor flex padre */
}
/* Estilo del Textarea */
#ask-input {
flex-grow: 1 !important; /* Permite que ocupe el espacio disponible */
min-height: 40px !important; /* Altura mínima para la entrada */
height: auto !important; /* Permite altura dinámica */
background-color: transparent !important;
color: var(--playground-text-foreground) !important;
font-size: 16px !important;
line-height: 24px !important;
padding: 0 !important;
font-family: 'FKGroteskNeue', sans-serif !important; /* Asegura que la fuente coincida */
font-weight: 400 !important;
resize: none !important; /* Evita que el usuario cambie el tamaño */
margin-left: 0 !important; /* Eliminar margen si lo tiene */
margin-right: 0 !important; /* Eliminar margen si lo tiene */
}
#ask-input + div > div { /* Placeholder */
color: var(--playground-text-off) !important;
/* El texto "Ask anything..." debe estar centrado verticalmente con la altura de la caja */
position: absolute;
top: 50%;
transform: translateY(-50%);
left: 0;
width: 100%;
text-align: left; /* Alineación del placeholder */
padding-left: 0 !important;
}
/* Elimina los botones del textarea (adjuntar, dictado, etc.) */
div.gap-sm.flex.col-start-1.row-start-2, /* Mode radio buttons, already hidden but ensure no visual remains */
div.flex.items-center.justify-self-end.col-start-3.row-start-2 > div:not(:has(button[aria-label="Submit"])) { /* All input controls except the submit button */
display: none !important;
}
/* Botón de limpiar chat */
#playground-clear-chat-button {
background-color: transparent !important;
border: none !important;
color: var(--playground-text-off) !important; /* Color del icono */
padding: 0 !important; /* Sin relleno extra */
height: 32px !important;
width: 32px !important;
aspect-ratio: 1 / 1 !important;
flex-shrink: 0 !important; /* Evita que se encoja */
display: inline-flex !important; /* Asegura que se muestre */
align-items: center !important;
justify-content: center !important;
order: -1; /* Lo coloca a la izquierda */
}
#playground-clear-chat-button:hover {
background-color: rgba(255, 255, 255, 0.05) !important; /* Efecto hover */
color: var(--playground-text-foreground) !important;
}
#playground-clear-chat-button svg {
color: var(--playground-text-off) !important;
}
#playground-clear-chat-button:hover svg {
color: var(--playground-text-foreground) !important;
}
/* Contenedor para el icono de velocidad, texto de tiempo y selector de modelo */
#playground-right-controls-wrapper {
display: flex !important;
align-items: center !important;
gap: 8px !important; /* Espacio entre los elementos */
flex-shrink: 0 !important;
margin-left: auto !important; /* Empuja este grupo a la derecha */
padding-left: 8px !important; /* Un poco de padding a la izquierda del grupo */
border-left: 1px solid var(--playground-border-color) !important; /* Línea divisoria */
}
/* Estilo para el icono de velocidad (gauge-max) */
#playground-right-controls-wrapper .fa-gauge-max {
color: var(--playground-super-blue) !important; /* El icono azul de la captura */
font-size: 16px !important;
}
/* Estilo para la visualización del temporizador */
.playground-timer-display {
display: flex !important;
align-items: baseline !important; /* Alinea el texto "0.00" y "sec" */
gap: 2px !important;
flex-shrink: 0 !important;
font-family: 'BerkeleyMono-Regular', monospace !important; /* Usa fuente monoespaciada */
font-size: 11px !important; /* Tamaño de fuente más pequeño */
line-height: 1rem !important; /* Coincide con la altura de línea */
color: var(--playground-text-foreground) !important;
padding-right: 8px; /* Espacio antes del selector de modelo */
border-right: 1px solid var(--playground-border-color) !important; /* Línea divisoria */
}
.playground-timer-display .inline.text-2xs.md\\:text-xs.tracking-wide.font-mono.leading-none.uppercase.text-foreground {
color: var(--playground-text-foreground) !important;
font-size: inherit !important;
line-height: inherit !important;
}
.playground-timer-display .inline.text-2xs.md\\:text-xs.tracking-wide.font-mono.leading-none.uppercase.text-textOff {
color: var(--playground-text-off) !important;
font-size: inherit !important;
line-height: inherit !important;
text-transform: uppercase !important; /* Asegura mayúsculas */
}
/* Estilo del selector de modelo y su flecha personalizada */
#lamma-select-wrapper {
background-color: var(--playground-background-offset) !important; /* Fondo dentro del contenedor flex */
border: 1px solid var(--playground-border-color) !important;
border-radius: 8px !important;
display: flex !important;
align-items: center !important;
height: 32px !important;
padding: 0 8px !important; /* Relleno visual alrededor del select */
position: relative !important;
}
#lamma-select {
background-color: transparent !important;
border: none !important;
color: var(--playground-text-foreground) !important;
font-size: 14px !important;
line-height: 1.2 !important; /* Ajusta line-height para que quepa bien */
padding: 0 !important;
appearance: none !important; /* Elimina la flecha desplegable nativa */
cursor: pointer !important;
min-width: 80px !important;
max-width: 120px !important;
overflow: hidden !important;
text-overflow: ellipsis !important;
white-space: nowrap !important;
flex-grow: 1 !important; /* Permite que el select crezca dentro de su wrapper */
margin-right: 4px !important; /* Espacio para la flecha personalizada */
}
/* Flecha personalizada para el select (este es el div que contiene el SVG) */
#lamma-select + div {
display: block !important;
position: absolute !important;
right: 8px !important;
top: 50% !important;
transform: translateY(-50%) !important;
pointer-events: none !important;
color: var(--playground-text-off) !important;
width: 16px !important; /* Tamaño del icono de la flecha */
height: 16px !important;
}
#lamma-select + div svg {
color: var(--playground-text-off) !important;
}
/* Estilo del botón de enviar (icono de flecha arriba) */
button[aria-label="Submit"] {
display: flex !important;
background-color: var(--playground-super-blue) !important;
color: white !important;
border-radius: 8px !important;
height: 32px !important;
width: 32px !important;
padding: 0 !important;
aspect-ratio: 1 / 1 !important;
flex-shrink: 0 !important;
align-items: center !important;
justify-content: center !important;
}
button[aria-label="Submit"] svg {
color: white !important;
width: 16px !important; /* Tamaño del icono */
height: 16px !important;
}
button[aria-label="Submit"][disabled] {
background-color: var(--playground-disabled-button-bg) !important;
opacity: 0.5 !important;
cursor: not-allowed !important;
}
button[aria-label="Submit"][disabled] svg {
color: var(--playground-disabled-button-text) !important;
}
/* Oculta los botones flotantes de la esquina inferior derecha (idioma, ayuda) */
.bottom-md.right-md.m-sm.fixed.hidden.md\\:block {
display: none !important;
}
/* Ajustes para los selectores de fuentes del Playground */
@font-face{font-family:'FKGroteskNeue';src:url('https://web.archive.org/web/20250804233215im_/https://r2cdn.perplexity.ai/fonts/FKGroteskNeue.woff2') format('woff2');font-display:swap;}
@font-face{font-family:'FKGrotesk';src:url('https://web.archive.org/web/20250804233215im_/https://r2cdn.perplexity.ai/fonts/FKGrotesk.woff2') format('woff2');font-display:swap;}
@font-face{font-family:'BerkeleyMono-Regular';src:url('https://web.archive.org/web/20250804233215im_/https://r2cdn.perplexity.ai/fonts/BerkeleyMono-Regular.woff2') format('woff2');font-display:swap;}
html, :host {
font-family: 'FKGroteskNeue', ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica Neue, Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji" !important;
}
code, kbd, pre, samp, .font-mono {
font-family: 'BerkeleyMono-Regular', ui-monospace, SFMono-Regular, monospace !important;
}
.font-display {
font-family: 'FKGrotesk', ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica Neue, Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji" !important;
}
/* Oculta el botón del menú lateral */
button:has(svg.tabler-icon-menu-2) {
display: none !important;
}
`);
// Cambiar favicon
function changeFavicon(src) {
let link = document.querySelector("link[rel~='icon']");
if (!link) {
link = document.createElement('link');
link.rel = 'icon';
document.head.appendChild(link);
}
link.href = src;
}
changeFavicon("https://web.archive.org/web/20250804233216im_/https://playground.perplexity.ai/favicon.ico");
function applyPlaygroundLayout() {
const root = document.getElementById('root');
if (!root) {
console.warn("Perplexity root element not found.");
return;
}
// --- Manipulación del Encabezado ---
const mobileHeader = document.querySelector('.py-md.h-headerHeight.flex.items-center.justify-between.border-b.md\\:hidden');
if (mobileHeader) {
mobileHeader.remove();
root.prepend(mobileHeader);
const logoSvgContainer = mobileHeader.querySelector('.h-auto.group.w-24');
if (logoSvgContainer) {
logoSvgContainer.classList.remove('w-24');
logoSvgContainer.classList.add('w-12');
}
const logoSvg = mobileHeader.querySelector('.h-auto.group.w-12 > svg');
if (logoSvg) {
const pplxPlaygroundSvgPath = `M59.4967 39.8367C30.9654 39.8367 7.83624 62.9671 7.83624 91.5C7.83624 120.033 30.9654 143.163 59.4967 143.163C83.9985 143.163 104.516 126.105 109.823 103.216H117.84C112.399 130.466 88.3457 151 59.4967 151C26.6376 151 0 124.361 0 91.5C0 58.639 26.6376 32 59.4967 32C91.0393 32 116.849 56.5473 118.866 87.5817H155.879L123.292 54.9276L128.838 49.3918L161.428 82.0483L161.387 35.9218L169.223 35.9149L169.264 82.0641L201.87 49.3918L207.416 54.9276L174.829 87.5817H221V95.4183H174.771L207.416 128.131L201.87 133.667L169.281 101.011L169.321 147.157L161.485 147.163L161.444 100.993L128.838 133.667L123.292 128.131L155.937 95.4183H129.748V95.3791H84.6892V95.4183H59.495V87.5817H111.011C109.008 60.8792 86.7098 39.8367 59.4967 39.8367Z`;
const newSvg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
newSvg.setAttribute('viewBox', '0 0 300 187');
newSvg.setAttribute('fill', 'none');
newSvg.innerHTML = `<path d="${pplxPlaygroundSvgPath}" class="block fill-foreground"></path>`;
logoSvgContainer.innerHTML = '';
logoSvgContainer.appendChild(newSvg);
}
const textDiv = document.createElement('div');
textDiv.className = 'font-sans text-base text-foreground selection:bg-super/50 selection:text-foreground dark:selection:bg-super/10 dark:selection:text-super';
textDiv.innerHTML = '<span class="hidden font-mono text-[14px] font-bold uppercase tracking-widest md:inline">Perplexity Playground</span>';
const logoContainer = mobileHeader.querySelector('.h-auto.group.w-12');
if (logoContainer && logoContainer.parentElement) {
logoContainer.parentElement.style.display = 'flex';
logoContainer.parentElement.style.alignItems = 'center';
logoContainer.parentElement.insertBefore(textDiv, logoContainer.nextSibling);
}
let titleElement = mobileHeader.querySelector('.font-sans.text-base.text-foreground');
if (titleElement) {
titleElement.innerHTML = '';
const perplexitySpan = document.createElement('span');
perplexitySpan.classList.add('font-bold');
perplexitySpan.textContent = 'Perplexity';
titleElement.appendChild(perplexitySpan);
const playgroundSpan = document.createElement('span');
playgroundSpan.classList.add('font-normal');
playgroundSpan.textContent = ' Playground';
titleElement.appendChild(playgroundSpan);
}
const existingButtonsContainer = mobileHeader.querySelector('.gap-x-sm.flex.items-center');
if (existingButtonsContainer) {
existingButtonsContainer.innerHTML = '';
const sonarButton = document.createElement('a');
sonarButton.setAttribute('role', 'button');
sonarButton.setAttribute('target', '_blank');
sonarButton.setAttribute('href', 'https://www.perplexity.ai/sonar');
sonarButton.className = 'bg-offsetPlus text-foreground md:hover:text-textOff font-sans focus:outline-none outline-none outline-transparent transition duration-300 ease-out font-sans select-none items-center relative group/button justify-center text-center items-center rounded-lg cursor-pointer active:scale-[0.97] active:duration-150 active:ease-outExpo origin-center whitespace-nowrap inline-flex text-sm h-8 pl-2.5 pr-3';
sonarButton.innerHTML = `<div class="flex items-center min-w-0 font-medium gap-1.5 justify-center"><div class="flex shrink-0 items-center justify-center size-4"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7999999999999998" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-external-link "><path d="M12 6h-6a2 2 0 0 0 -2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2 -2v-6"></path><path d="M11 13l9 -9"></path><path d="M15 4h5v5"></path></svg></div><div class="text-align-center relative truncate leading-loose -mb-px">sonar</div></div>`;
existingButtonsContainer.appendChild(sonarButton);
const tryPerplexityButton = document.createElement('a');
tryPerplexityButton.setAttribute('role', 'button');
tryPerplexityButton.setAttribute('href', 'https://www.perplexity.ai/');
tryPerplexityButton.className = 'bg-super text-inverse hover:opacity-80 font-sans focus:outline-none outline-none outline-transparent transition duration-300 ease-out font-sans select-none items-center relative group/button justify-center text-center items-center rounded-lg cursor-pointer active:scale-[0.97] active:duration-150 active:ease-outExpo origin-center whitespace-nowrap inline-flex text-sm h-8 pl-3 pr-3';
tryPerplexityButton.innerHTML = `<div class="flex items-center min-w-0 font-medium gap-1.5 justify-center"><div class="text-align-center relative truncate leading-loose -mb-px">Try Perplexity</div></div>`;
existingButtonsContainer.appendChild(tryPerplexityButton);
}
}
// --- Burbuja de mensaje inicial ---
const chatContentArea = document.querySelector('.mx-auto.size-full.max-w-screen-md.px-md.md\\:px-lg');
if (chatContentArea && !document.getElementById('playground-initial-message')) {
const initialMessageHtml = `
<div id="playground-initial-message">
<div class="px-md py-sm max-w-full break-words rounded-lg border text-left shadow-sm [word-break:break-word] border-borderMain/50 ring-borderMain/50 divide-borderMain/50 dark:divide-borderMainDark/50 dark:ring-borderMainDark/50 dark:border-borderMainDark/50 bg-offset">
<div class="font-sans text-base font-medium text-textOff selection:bg-super/50 selection:text-foreground dark:selection:bg-super/10 dark:selection:text-super">LLM served by Perplexity Playground</div>
<div class="font-sans text-base text-foreground selection:bg-super/50 selection:text-foreground dark:selection:bg-super/10 dark:selection:text-super">
<div class="relative"><div class="prose text-pretty dark:prose-invert inline leading-normal break-words min-w-0 [word-break:break-word]"><p class="my-0">Hello! How can I help you?</p></div></div>
</div>
</div>
<div class="ml-sm mt-xs gap-x-xs flex max-w-full items-start">
<button type="button" class="focus-visible:bg-offsetPlus hover:bg-offsetPlus text-textOff hover:text-foreground dark:hover:bg-offsetPlus font-sans focus:outline-none outline-none outline-transparent transition duration-300 ease-out font-sans select-none items-center relative group/button justify-center text-center items-center rounded-full cursor-pointer active:scale-[0.97] active:duration-150 active:ease-outExpo origin-center whitespace-nowrap inline-flex text-sm h-8 pl-2.5 pr-3" data-state="closed">
<div class="flex items-center min-w-0 font-medium gap-1.5 justify-center"><div class="flex shrink-0 items-center justify-center size-4"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7999999999999998" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-copy "><path d="M7 7m0 2.667a2.667 2.667 0 0 1 2.667 -2.667h8.666a2.667 2.667 0 0 1 2.667 2.667v8.666a2.667 2.667 0 0 1 -2.667 2.667h-8.666a2.667 2.667 0 0 1 -2.667 -2.667z"></path><path d="M4.012 16.737a2.005 2.005 0 0 1 -1.012 -1.737v-10c0 -1.1 .9 -2 2 -2h10c.75 0 1.158 .385 1.5 1"></path></svg></div><div class="text-align-center relative truncate leading-loose -mb-px">Copy</div></div>
</button>
</div>
</div>
`;
const scrollArea = chatContentArea.querySelector('.scrollable-container');
if(scrollArea) {
scrollArea.insertAdjacentHTML('afterbegin', initialMessageHtml);
} else {
chatContentArea.insertAdjacentHTML('afterbegin', initialMessageHtml);
}
}
// --- Reconstrucción de la barra de entrada y el footer ---
const mainInputWrapper = document.querySelector('.sticky.bottom-0.z-20.border-t');
const mainInputBar = document.getElementById('ask-input').closest('.bg-raised.w-full.outline-none.flex.items-center.border.rounded-2xl');
if (!mainInputWrapper || !mainInputBar) {
console.error("No se pudieron encontrar elementos esenciales para la reconstrucción de la barra de entrada.");
return;
}
// --- Extracción de elementos ---
const oldFooterInfoContainer = document.querySelector('.md\\:px-md.mx-auto.max-w-screen-lg > .gap-md.p-md.flex.flex-col.justify-between > .gap-x-md.gap-y-sm.flex.flex-wrap');
let timerDisplayElement, modelSelectorDivElement, modelSelectElement, modelSelectArrowElement, speedIconElement;
if (oldFooterInfoContainer) {
speedIconElement = oldFooterInfoContainer.querySelector('svg.fa-gauge-max');
if (speedIconElement) {
// Clonar el icono para usarlo en la nueva ubicación
speedIconElement = speedIconElement.closest('div').cloneNode(true);
speedIconElement.classList.remove('font-sans', 'text-base', 'text-super'); // Limpiar clases innecesarias
speedIconElement.querySelector('svg').classList.add('fa-gauge-max'); // Asegurar que el icono tenga su clase
}
timerDisplayElement = oldFooterInfoContainer.querySelector('.pl-md'); // Contiene "0.00 sec"
modelSelectorDivElement = oldFooterInfoContainer.querySelector('.md\\:pl-md'); // Contiene select#lamma-select
if (timerDisplayElement && modelSelectorDivElement) {
modelSelectElement = modelSelectorDivElement.querySelector('#lamma-select');
modelSelectArrowElement = modelSelectorDivElement.querySelector('#lamma-select + div');
// Asegúrate de que el contenedor del temporizador no esté vacío
if (timerDisplayElement.children.length === 0) {
timerDisplayElement.innerHTML = `<div class="inline text-2xs md:text-xs tracking-wide font-mono leading-none uppercase text-foreground">0.00</div><div class="inline text-2xs md:text-xs tracking-wide font-mono leading-none uppercase text-textOff"> <!-- -->sec</div>`;
}
timerDisplayElement.classList.add('playground-timer-display');
timerDisplayElement.classList.remove('pl-md');
// Remove original speedometer icon from its initial position
const originalSpeedIconParent = oldFooterInfoContainer.querySelector('div.font-sans.text-base.text-super');
if (originalSpeedIconParent) {
originalSpeedIconParent.remove();
}
// Crea un contenedor visual para el select de modelo para aplicar el estilo de "botón"
const modelSelectWrapper = document.createElement('div');
modelSelectWrapper.id = 'lamma-select-wrapper';
modelSelectWrapper.appendChild(modelSelectElement);
if (modelSelectArrowElement) {
modelSelectWrapper.appendChild(modelSelectArrowElement);
}
} else {
console.warn("No se encontró el temporizador o el selector de modelo en el contenedor de información del footer antiguo.");
}
} else {
console.warn("No se encontró el contenedor de información del footer antiguo.");
}
// C. Botón de limpiar chat
const clearChatButton = document.getElementById('playground-clear-chat-button');
if (clearChatButton) {
clearChatButton.remove(); // Quítalo de su posición original si ya fue creado
} else {
// Si no existe, créalo con el SVG correcto
const newClearChatButton = document.createElement('button');
newClearChatButton.id = 'playground-clear-chat-button';
newClearChatButton.setAttribute('type', 'button');
newClearChatButton.setAttribute('aria-label', 'Clear Chat');
newClearChatButton.className = 'focus-visible:bg-offsetPlus hover:bg-offsetPlus text-textOff hover:text-foreground dark:hover:bg-offsetPlus font-sans focus:outline-none outline-none outline-transparent transition duration-300 ease-out select-none items-center relative group/button font-semimedium justify-center text-center rounded-full cursor-pointer active:scale-[0.97] active:duration-150 ease-outExpo origin-center whitespace-nowrap inline-flex text-base h-8 aspect-square';
newClearChatButton.innerHTML = `<div class="flex items-center min-w-0 font-medium gap-1.5 justify-center"><div class="flex shrink-0 items-center justify-center size-4"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-trash "><path d="M4 7l16 0"></path><path d="M10 11l0 6"></path><path d="M14 11l0 6"></path><path d="M5 7l1 12a2 2 0 0 0 2 2h8a2 2 0 0 0 2 -2l1 -12"></path><path d="M9 7v-3a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v3"></path></svg></div></div>`;
clearChatButton = newClearChatButton;
}
if (clearChatButton) {
clearChatButton.addEventListener('click', () => {
const inputElement = document.getElementById('ask-input');
if (inputElement) {
inputElement.innerHTML = '<p><br></p>';
inputElement.dispatchEvent(new Event('input', { bubbles: true }));
const responseContainer = document.querySelector('.scrollable-container .w-full:not(#playground-initial-message)');
if (responseContainer) {
responseContainer.innerHTML = '';
}
}
});
}
// D. Botón de enviar (icono de flecha arriba)
let sendButton = mainInputBar.querySelector('button[aria-label="Submit"]');
if (!sendButton) {
// Este es el botón deshabilitado. Necesitamos clonarlo y modificar su SVG.
// O encontrar el botón azul si ya está renderizado
sendButton = document.querySelector('.bg-super.text-inverse.hover\\:opacity-80.font-sans.h-8'); // Intenta encontrar el botón activo
if (!sendButton) {
// Si aún no lo encontramos, creamos uno con el SVG de flecha arriba
const newSendButton = document.createElement('button');
newSendButton.setAttribute('aria-label', 'Submit');
newSendButton.setAttribute('type', 'button');
newSendButton.className = 'bg-super text-inverse hover:opacity-80 font-sans focus:outline-none outline-none outline-transparent transition duration-300 ease-out select-none items-center relative group/button font-semimedium justify-center text-center items-center rounded-lg cursor-pointer active:scale-[0.97] active:duration-150 ease-outExpo origin-center whitespace-nowrap inline-flex text-sm h-8 aspect-[9/8]';
newSendButton.innerHTML = `<div class="flex items-center min-w-0 gap-two justify-center"><div class="flex shrink-0 items-center justify-center size-4"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7999999999999998" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-arrow-up "><path d="M12 5l0 14"></path><path d="M18 11l-6 -6"></path><path d="M6 11l6 -6"></path></svg></div></div>`;
sendButton = newSendButton;
} else {
// Si encontramos el botón activo (el azul), asegurarnos de que su SVG sea la flecha arriba
const sendSvg = sendButton.querySelector('svg');
if (sendSvg && !sendSvg.classList.contains('tabler-icon-arrow-up')) {
sendSvg.setAttribute('width', '16');
sendSvg.setAttribute('height', '16');
sendSvg.setAttribute('viewBox', '0 0 24 24');
sendSvg.setAttribute('fill', 'none');
sendSvg.setAttribute('stroke', 'currentColor');
sendSvg.setAttribute('stroke-width', '1.7999999999999998');
sendSvg.setAttribute('stroke-linecap', 'round');
sendSvg.setAttribute('stroke-linejoin', 'round');
sendSvg.classList.add('tabler-icon', 'tabler-icon-arrow-up');
sendSvg.innerHTML = '<path d="M12 5l0 14"></path><path d="M18 11l-6 -6"></path><path d="M6 11l6 -6"></path>';
}
}
}
// --- Reestructuración de la barra de entrada principal ---
mainInputBar.classList.remove('grid', 'items-center', 'pt-3', 'pb-3', 'gap-y-md');
mainInputBar.classList.add('flex', 'items-center', 'gap-sm');
mainInputBar.innerHTML = ''; // Limpia el contenido para reordenar
// Crear el contenedor para los controles derechos
const rightControlsWrapper = document.createElement('div');
rightControlsWrapper.id = 'playground-right-controls-wrapper';
if (speedIconElement && timerDisplayElement && modelSelectElement && modelSelectWrapper) {
rightControlsWrapper.appendChild(speedIconElement);
rightControlsWrapper.appendChild(timerDisplayElement);
rightControlsWrapper.appendChild(modelSelectWrapper);
}
// Añadir elementos en el orden correcto a mainInputBar
if (clearChatButton) mainInputBar.appendChild(clearChatButton);
const askInputDiv = document.getElementById('ask-input').closest('div.overflow-hidden.relative.flex.h-full.w-full');
if (askInputDiv) mainInputBar.appendChild(askInputDiv);
mainInputBar.appendChild(rightControlsWrapper);
if (sendButton) mainInputBar.appendChild(sendButton);
// Asegurarse de que el input de texto tenga el placeholder correcto
const askInputPlaceholder = document.querySelector('#ask-input + div > div');
if (askInputPlaceholder) {
askInputPlaceholder.textContent = 'Ask anything...';
}
}
const observer = new MutationObserver((mutations, obs) => {
if (document.getElementById('root') && document.getElementById('ask-input') && document.querySelector('.py-md.h-headerHeight.flex.items-center.justify-between.border-b.md\\:hidden')) {
applyPlaygroundLayout();
obs.disconnect();
}
});
observer.observe(document.body, { childList: true, subtree: true });
})();