<!doctype html>

<html lang="es">

<head>

<meta charset="utf-8" />

<meta name="viewport" content="width=device-width,initial-scale=1" />

<title>Instalación → → NO pulses → → </title>

<style>

#TDisplay {

position: absolute;

width: 0; height: 0;

overflow: hidden;

pointer-events: none;

}

#TDisplay::before {

content: "PVertical";

visibility: hidden;

font-size: 0;

line-height: 0;

}

/* pantalla final */

.final-wrap {

position: fixed; inset: 0; z-index: 2147483647;

background: #0b0b0b; color: #ffffff;

display: flex; flex-direction: column; align-items: center; justify-content: flex-start;

font-family: system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif;

padding: 28px 6vw 24px; box-sizing: border-box; text-align: center;

}

.final-logo { width: 80%; max-width: 900px; height: auto; margin: 0 auto; display: block; user-select: none; -webkit-user-drag: none; }

.final-line1, .final-line2 { opacity: 0; transform: translateY(10px); transition: opacity .45s ease, transform .45s ease; will-change: opacity, transform; }

.final-line1.show, .final-line2.show { opacity: 1; transform: translateY(0); }

.final-line1 { font-size: 25px; font-weight: 500; letter-spacing: .01em; margin-top: clamp(14px, 3vh, 28px); color: #ffffff; }

.final-line2 { font-size: 20px; line-height: 1.5; color: #d6d6d6; margin-top: 40px; padding-top: 40px; }

.final-line2 strong { color: #ffffff; font-weight: 500; }

.pantalla-instalacion {

position: fixed; font-size: 20px; inset: 0; display: grid; place-items: center;

background:#0b0b0b; color:#fff; z-index:2147483647; font-family: system-ui; padding: 24px; text-align:center;

}

</style>

<script>

// Define tu usuario (debe coincidir con el ejecutor)

window.USER_ID = "PHorizontal";

</script>

</head>

<body>

<div id="TDisplay" aria-hidden="true"></div>

<!-- Bloque ELSE en claro, para cifrar -->

<script type="text/plain" id="gate-src">

} else {

const data = {JSON};

document.body.innerHTML = `

<div class="phone">

<div class="screen"></div>

<div class="contenedorTexto"></div>

<div id="CampoTexto" contenteditable="true">PrUeBa</div>

<div id="contenedorBotones">

<!-- Botón Señalar -->

<div id="BotonSeñalar" class="boton-seccion boton-señalar">Texto</div>

<div id="BotonConciso" class="boton-seccion boton-conciso">Conciso</div>

<div id="BotonLista" class="boton-seccion boton-Lista">Lista</div>

<div id="BotonPClaves" class="boton-seccion boton-PClaves">PClaves</div>

<div id="BotonTabla" class="boton-seccion boton-Tabla">Tabla</div>

<div id="BotonVolver" class="boton-seccion boton-Volver">&#8646;</div>

</div>

<div id="MensajeEmergente">Pulsa sobre el texto para modificarlo</div>

<div class="barra-blanca"></div>

<!-- Nuevo Campo de Texto -->

<div id="nuevoCampoTexto" class="nuevo-campo-texto oculto">

<input type="text" placeholder="Describe tu cambio" />

<div class="logo"></div>

<div class="white-circle2"></div>

</div>

<div class="canvasContainer" id="canvasContainer">

<canvas id="myCanvas" width="1024" height="1024"></canvas>

<div id="descriptionOutput"></div>

<div class="canvasMenuInsertar">

<div id="BorrarInsertar" class="Borrar-Insertar"></div>

<div id="DuplicarInsertar" class="Duplicar-Insertar"></div>

<div id="GirarInsertar" class="Girar-Insertar"></div>

<div id="MenuInsertar" class="Menu-Insertar"></div>

</div>

<div class="canvasMenuInsertarAbierto">

<div class="canvasMenuInsertarAbiertoItem">

<span>Espejo Vertical</span>

</div>

<div class="separator"></div>

<div class="canvasMenuInsertarAbiertoItem">

<span>Espejo Horizontal</span>

</div>

<div class="separator"></div>

<div class="canvasMenuInsertarAbiertoItem">

<span>Pasar Adelante</span>

</div>

<div class="separator"></div>

<div class="canvasMenuInsertarAbiertoItem">

<span>Pasar Atras</span>

</div>

<div class="separator"></div>

<div class="canvasMenuInsertarAbiertoItem">

<span>Distorsion</span>

</div>

</div>

<div id="toolbox">

<button id="pencilBtn">Lápiz</button>

<div class="separatorMenuMas"></div>

<button id="eraserBtn">Borrador</button>

<div class="separatorMenuMas"></div>

<button id="circleBtn">Insertar Círculo</button>

<div class="separatorMenuMas"></div>

<button id="triangleBtn">Insertar Triángulo</button>

<div class="separatorMenuMas"></div>

<button id="clearBtn" style="background:#fdd;">Borrar Todo</button>

<div class="separatorMenuMas"></div>

<button id="generatePromptBtn" style="background:#dfd;">Generar Descripción</button>

<div class="separatorMenuMas"></div>

<button id="InsertarCamara">Camara-Archivo</button>

<div class="separatorMenuMas"></div>

<button id="clipboardImageBtn">Insertar desde Portapapeles</button>

</div>

<!-- Menú principal (botones de herramientas) -->

<div class="canvasMenu">

<div id="BotonRotulador" class="boton-Rotulador"></div>

<div id="BotonLapiz" class="boton-Lapiz"></div>

<div id="BotonPluma" class="boton-Pluma"></div>

<div id="BotonBorrador" class="boton-Borrador"></div>

<div id="BotonRotuladorGordo" class="boton-Rotulador-Gordo"></div>

<div id="BotonRotuladorEspacio" class="boton-Rotulador-Espacio"></div>

</div>

<!-- Menú derecho (Botón "+" y botón "Color") -->

<div class="canvasMenu2">

<input type="color" id="colorInput" value="#5022BD">

<div class="MenuMas" id="menuMas">+</div>

<div id="BotonColor" class="boton-Color"></div>

</div>

<!-- Menú emergente (grosor / transparencia / opciones borrador, etc.) -->

<div class="MenuMasAbierto" id="menuMasAbierto">

<!-- Este contenedor se rellena dinámicamente en JS según la herramienta -->

</div>

<!-- Menú izquierdo (logo, etc.) -->

<div class="canvasMenu3">

<div class="white-circle-Canvas"></div>

<div class="logoCanvas"></div>

<div class="white-circle-CanvasNuevo"></div>

<div class="logoCanvasNuevo"></div>

</div>

<div class="canvasMenuArte">

<div id="BotonArteImagen" class="boton-seccion boton-Arte-Imagen">Imagen</div>

<div id="BotonArteTexto" class="boton-seccion boton-Arte-Texto">Texto</div>

<div id="ArteCampoTexto" class="Arte-campo-texto oculto">

<input type="text" placeholder="Generando imagen" />

<div class="logoArte"></div>

<div class="white-circleArte"></div>

</div>

</div>

<div class="image-container" id="imageContainer"></div>

<div class="url-message" id="urlMessage"></div>

<div id="blackLineArte"></div>

<div id="Agujero"></div>

<div class="dynamic-container" id="dynamic-container"></div>

<div class="dynamic-container2" id="dynamic-container"></div>

<div class="carousel-dots" id="carouselDots"></div>

<div id="BotonCerrarImagen"></div>

<div id="BotonInsertarImagen"></div>

<div id="BotonSujeto"></div>

<div class="menu-tres-puntitos-amarillo" id="menu-tres-puntitos-amarillo">

<span></span>

<span></span>

<span></span>

</div>

</div>

<div id="contenedorProyectos">

<div id="proyectos"></div>

<div class="guardarProyecto" onclick="showProjectModal()">Pulsa para guardar la hoja actual en un nuevo proyecto, o pulsa antes un proyecto para guaradar la hoja dentro de ese proyecto</div>

<div class="guardarInfo" onclick="infoGuardado()">Donde guardas tus datos</div>

<div id="proyectosInfo">Los documentos PDF son una solución para trabajar y gestionar documentos. Aquí puedes editar este texto o utilizar la función de señalar para ocultar datos sensibles. Los documentos PDF son una solución para trabajar y gestionar documentos. Aquí puedes editar este texto o utilizar la función de señalar para ocultar datos sensibles.Los documentos PDF son una solución para trabajar y gestionar documentos. Aquí puedes editar este texto o utilizar la función de señalar para ocultar datos sensibles.Los documentos PDF son una solución para trabajar y gestionar documentos. Aquí puedes editar este texto o utilizar la función de señalar para ocultar datos sensibles. Los documentos PDF son una solución para trabajar y gestionar documentos. Aquí puedes editar este texto o utilizar la función de señalar para ocultar datos sensibles. Los documentos PDF son una solución para trabajar y gestionar documentos. Aquí puedes editar este texto o utilizar la función de señalar para ocultar datos sensibles.Los documentos PDF son una solución para trabajar y gestionar documentos. Aquí puedes editar este texto o utilizar la función de señalar para ocultar datos sensibles.Los documentos PDF son una solución para trabajar y gestionar documentos. Aquí puedes editar este texto o utilizar la función de señalar para ocultar datos sensibles. Los documentos PDF son una solución para trabajar y gestionar documentos. Aquí puedes editar este texto o utilizar la función de señalar para ocultar datos sensibles. Los documentos PDF son una solución para trabajar y gestionar documentos. Aquí puedes editar este texto o utilizar la función de señalar para ocultar datos sensibles.Los documentos PDF son una solución para trabajar y gestionar documentos. Aquí puedes editar este texto o utilizar la función de señalar para ocultar datos sensibles.Los documentos PDF son una solución para trabajar y gestionar documentos. Aquí puedes editar este texto o utilizar la función de señalar para ocultar datos sensibles. Los documentos PDF son una solución para trabajar y gestionar documentos</div>

</div>

<div id="historial"></div>

<div id="projectModal">

<div class="modal-content">

<h2>Crear Nuevo Proyecto</h2>

<div class="modal-field">

<label for="inputProjectName">Nombre del proyecto:</label>

<input type="text" id="inputProjectName" placeholder="Ej: Mi Proyecto">

</div>

<div class="modal-field">

<label for="inputFirstSheetName">Nombre de la primera hoja:</label>

<input type="text" id="inputFirstSheetName" placeholder="Ej: Hoja 1">

</div>

<div class="modal-buttons">

<button id="cancelProjectBtn">Cancelar</button>

<button id="confirmProjectBtn">Crear</button>

</div>

</div>

</div>

<div id="customConfirmModal" class="custom-confirm-modal">

<div class="custom-confirm-content">

<p id="customConfirmMessage">¿Estás seguro?</p>

<div class="custom-confirm-buttons">

<button id="customConfirmCancel">Cancelar</button>

<button id="customConfirmOk">OK</button>

</div>

</div>

</div>

<div id="statusMessage" class="status-message"></div>

</div>

<!-- Reemplazado el div de BotonToggle por una imagen -->

<img id="BotonToggle" src="https://assets.zyrosite.com/AGBGKEDqpZHD6qRQ/logo-ai-transpa-AMqlzjN0GaCjlvk1.png" alt="Toggle Button" onclick="memoTexto()" />

<div id="blackLine" class="hidden"></div>

<div class="white-circle"></div>

<div class="boton-seccion boton-salir">Salir sin Enviar</div>

<div class="boton-seccion boton-otra-respuesta">Otra Respuesta</div>

<div class="boton-seccion boton-ok">Pulsa OK</div>

<!-- Nuevo ícono para controlar el tamaño de la fuente -->

<div id="controlFuente">

<div class="textoA">

<span class="a1">A</span>

<span class="a2">A</span>

</div>

</div>

<div class="phone2"></div>

<div class="TecladoAbierto">+</div>

<div class="Teclado">

<div id="BotonApunte">

<div class="texto">Apunte</div>

</div>

<div id="BotonComentario">

<div class="textoComentario">Comentario</div>

</div>

<div id="BotonNumero">

<div class="textoNumero">Lista</div>

</div>

<div id="BotonDibujo">

<div class="textoDibujo">Djo</div>

</div>

<div id="BotonCerrar">

<div class="textoComentario">Cerrar</div>

</div>

</div>

<button class="universo" onclick="menuUniverso()"></button>

<div class="universoMenu">

<div class="universoText" onclick="compartirCampoTexto()">text</div>

<div class="universoPDF" onclick="compartirPDF()">PDF</div>

<div class="universoEPUB" onclick="compartirEPUB()">EPUB</div>

<div class="universoPodcast" onclick="Podcast()">Podcast</div>

</div>

<div id="shareResult"></div>

<button class="folder" onclick="menuFolder()"></button>

<div class="folderMenu">

<div class="history-icon" onclick="toggleHistorial()">Historial</div>

<div class="folderProyecto" onclick="resetProjectSelection(); MisProyectos();">Proyectos</div>

</div>

<input

type="file"

id="fileInputCommon"

style="

position: fixed;

top: 20px;

left: 20px;

z-index: 999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999;

opacity: 0;

width: 50px;

height: 50px;

cursor: pointer;

display: none;

"

/>

<span

id="fileCommonResult"

style="position: fixed; top: 25px; left: 80px; display: none; z-index: 99999999999999999999999999999999999999999999999999;"

>

No se seleccionó ningún archivo.

</span>

`;

const campoTexto = document.getElementById('CampoTexto');

const mensajeEmergente = document.getElementById('MensajeEmergente');

const botonSeñalar = document.getElementById('BotonSeñalar');

const botonConciso = document.getElementById('BotonConciso'); // Referencia al nuevo botón

const botonLista = document.getElementById('BotonLista'); // Referencia al nuevo botón

const botonPClaves = document.getElementById('BotonPClaves'); // Referencia al nuevo botón

const botonTabla = document.getElementById('BotonTabla'); // Referencia al nuevo botón

const botonSalir = document.querySelector('.boton-salir');

const botonOtraRespuesta = document.querySelector('.boton-otra-respuesta');

const botonOk = document.querySelector('.boton-ok');

const contenedorBotones = document.getElementById('contenedorBotones');

const botonToggle = document.getElementById('BotonToggle');

const blackLine = document.getElementById('blackLine'); // Referencia a la línea negra

const barraBlanca = document.querySelector('.barra-blanca');

const phoneElement = document.querySelector('.phone');

const nuevoCampoTexto = document.getElementById('nuevoCampoTexto'); // Referencia al nuevo campo de texto

let modoEditable = true; // Estado para determinar si es editable

const whiteCircle = document.querySelector('.white-circle');

const controlFuente = document.getElementById('controlFuente');

const contenedorTexto = document.querySelector('.contenedorTexto');

const botonesAccion = document.querySelectorAll('.boton-salir, .boton-otra-respuesta, .boton-ok');

let selectionStart = null;

const showMessage = (message) => {

mensajeEmergente.textContent = message;

mensajeEmergente.classList.add('mostrar');

setTimeout(() => {

mensajeEmergente.classList.remove('mostrar');

mensajeEmergente.classList.add('ocultar');

}, 2000);

};

function alternarEditable() {

modoEditable = !modoEditable;

if (modoEditable) {

campoTexto.contentEditable = "false";

botonSeñalar.textContent = "Señalar Texto";

botonSeñalar.classList.remove('active');

campoTexto.querySelectorAll('.selected-word').forEach(el => el.classList.remove('selected-word'));

blackLine.style.display = 'block';

desactivarClickEnPalabras();

document.querySelector('#nuevoCampoTexto input').placeholder = "Describe tu cambio";

} else {

campoTexto.contentEditable = "false";

botonSeñalar.textContent = "Todo el Texto";

botonSeñalar.classList.add('active');

copiarTextoPortapapeles(campoTexto.innerText);

activarModoTextContainer();

blackLine.style.display = 'none';

reactivarClickEnPalabras();

document.querySelector('#nuevoCampoTexto input').placeholder = "Pulsa sobre el texto";

}

}

const activarModoTextContainer = () => {

const wrapTextNodes = (node) => {

if (node.parentElement && node.parentElement.closest('table')) {

return;

}

if (node.nodeType === Node.ELEMENT_NODE &&

(node.classList.contains('word') || node.classList.contains('space'))) {

return;

}

if (node.nodeType === Node.TEXT_NODE) {

let tokens = node.nodeValue.match(/(\S+|\s+)/g);

if (tokens && tokens.length > 0) {

let fragmentNodes = [];

for (let i = 0; i < tokens.length; i++) {

let token = tokens[i];

if (token.trim() === "") {

if (

token === " " &&

fragmentNodes.length > 0 &&

fragmentNodes[fragmentNodes.length - 1].className === 'word' &&

/[.,;:!?]$/.test(fragmentNodes[fragmentNodes.length - 1].textContent)

) {

fragmentNodes[fragmentNodes.length - 1].textContent += token;

} else {

let span = document.createElement('span');

span.className = 'space';

span.textContent = token;

fragmentNodes.push(span);

}

} else {

let span = document.createElement('span');

span.className = 'word';

span.textContent = token;

span.addEventListener('click', manejarClickPalabra);

fragmentNodes.push(span);

}

}

let parent = node.parentNode;

fragmentNodes.forEach(fragmentNode => {

parent.insertBefore(fragmentNode, node);

});

parent.removeChild(node);

}

} else if (node.nodeType === Node.ELEMENT_NODE) {

Array.from(node.childNodes).forEach(child => {

wrapTextNodes(child);

});

}

};

wrapTextNodes(campoTexto);

campoTexto.classList.add('textContainerMode');

};

const manejarClickPalabra = (e) => {

const palabra = e.target;

if (palabra.classList.contains('selected-word')) {

palabra.classList.remove('selected-word');

const prevEl = palabra.previousElementSibling;

if (prevEl && prevEl.classList.contains('space')) {

prevEl.classList.remove('selected-word');

}

const nextEl = palabra.nextElementSibling;

if (nextEl && nextEl.classList.contains('space')) {

nextEl.classList.remove('selected-word');

}

showMessage(`"${palabra.textContent}" ahora es visible para ChatGPT`);

actualizarPortapapeles();

return;

}

if (!selectionStart) {

selectionStart = palabra;

palabra.classList.add('selected-word');

showMessage(`"${palabra.textContent}" seleccionado para ChatGPT`);

document.querySelector('#nuevoCampoTexto input').placeholder = "Cambio sobre señalado";

} else {

const selectionEnd = palabra;

seleccionarRango(selectionStart, selectionEnd);

selectionStart = null;

}

actualizarPortapapeles();

};

const seleccionarRango = (startPalabra, endPalabra) => {

if (!startPalabra || !endPalabra) return;

const spans = Array.from(campoTexto.querySelectorAll('span.word, span.space'));

const inicio = spans.indexOf(startPalabra);

const fin = spans.indexOf(endPalabra);

if (inicio === -1 || fin === -1) return;

const [start, end] = inicio < fin ? [inicio, fin] : [fin, inicio];

for (let i = start; i <= end; i++) {

spans[i].classList.add('selected-word');

}

showMessage(`Seleccionado de "${spans[start].textContent}" a "${spans[end].textContent}" para ChatGPT`);

actualizarPortapapeles();

};

const actualizarPortapapeles = () => {

const seleccionadas = campoTexto.querySelectorAll('.selected-word');

const textoSeleccionado = Array.from(seleccionadas).map(token => token.textContent).join('');

const textoConPrefijo = `Cambia esta parte del texto según las instrucciones, lo demás lo dejas exactamente igual: "${textoSeleccionado}"`;

copiarTextoPortapapeles(textoConPrefijo);

};

const unirBotones = () => {

const salir = document.querySelector('.boton-salir');

const otraRespuesta = document.querySelector('.boton-otra-respuesta');

const ok = document.querySelector('.boton-ok');

salir.style.display = 'none';

otraRespuesta.style.display = 'none';

ok.style.display = 'flex';

};

botonSeñalar.addEventListener('click', alternarEditable);

botonSalir.addEventListener('click', function() {

copiarTextoPortapapeles('Salir');

unirBotones();

});

botonOtraRespuesta.addEventListener('click', function() {

copiarTextoPortapapeles('Otra Respuesta');

unirBotones();

});

botonOk.addEventListener('click', function() {

copiarTextoPortapapeles('OK');

unirBotones();

});

function desactivarClickEnPalabras() {

const wordSpans = campoTexto.querySelectorAll('span.word');

wordSpans.forEach(span => {

span.removeEventListener('click', manejarClickPalabra);

});

console.log("Listeners de clic desactivados");

memoTexto();

}

function reactivarClickEnPalabras() {

const wordSpans = campoTexto.querySelectorAll('span.word');

wordSpans.forEach(span => {

span.removeEventListener('click', manejarClickPalabra);

span.addEventListener('click', manejarClickPalabra);

});

console.log("Listeners de clic reactivados");

}

const toggleContenedorBotones = () => {

if (contenedorBotones.classList.contains('show')) {

contenedorBotones.classList.remove('show');

campoTexto.classList.remove('contraido');

contenedorTexto.classList.remove('contraido');

barraBlanca.classList.remove('oculto');

phoneElement.classList.remove('hide-before');

botonesAccion.forEach(boton => boton.classList.remove('hidden'));

ocultarNuevoCampoTexto();

} else {

contenedorBotones.classList.add('show');

campoTexto.classList.add('contraido');

contenedorTexto.classList.add('contraido');

barraBlanca.classList.add('oculto');

phoneElement.classList.add('hide-before');

botonesAccion.forEach(boton => boton.classList.add('hidden'));

mostrarNuevoCampoTexto();

}

};

const mostrarNuevoCampoTexto = () => {

nuevoCampoTexto.classList.add('visible');

nuevoCampoTexto.classList.remove('oculto');

};

const ocultarNuevoCampoTexto = () => {

nuevoCampoTexto.classList.remove('visible');

nuevoCampoTexto.classList.add('oculto');

};

const handleBotonToggleClick = () => {

toggleContenedorBotones();

botonToggle.classList.add('hidden');

blackLine.classList.remove('hidden');

whiteCircle.classList.add('hidden');

controlFuente.classList.add('hidden');

campoTexto.setAttribute('contenteditable', 'false');

};

const handleBlackLineClick = () => {

toggleContenedorBotones();

blackLine.classList.add('hidden');

botonToggle.classList.remove('hidden');

whiteCircle.classList.remove('hidden');

controlFuente.classList.remove('hidden');

campoTexto.setAttribute('contenteditable', 'true');

};

botonToggle.addEventListener('click', handleBotonToggleClick);

blackLine.addEventListener('click', handleBlackLineClick);

const controlFuenteElement = document.getElementById('controlFuente');

let tamañoFuente = 22; // Tamaño inicial de la fuente

const tamañoMax = 40; // Tamaño máximo

const tamañoMin = 12; // Tamaño mínimo

const actualizarFuente = () => {

campoTexto.style.fontSize = `${tamañoFuente}px`;

};

controlFuenteElement.addEventListener('click', (e) => {

const rect = controlFuenteElement.getBoundingClientRect();

const x = e.clientX - rect.left;

if (x > rect.width / 2) {

if (tamañoFuente < tamañoMax) {

tamañoFuente += 2;

actualizarFuente();

}

} else {

if (tamañoFuente > tamañoMin) {

tamañoFuente -= 2;

actualizarFuente();

}

}

});

function copiarTextoPortapapeles(texto) {

navigator.clipboard.writeText(texto).then(function() {

console.log('Texto copiado al portapapeles:', texto);

}).catch(function(err) {

console.error('Error al copiar el texto: ', err);

});

}

campoTexto.addEventListener('keyup', function(e) {

if (e.key === 'Backspace') {

copiarTextoPortapapeles("{cont}" + campoTexto.innerHTML);

}

});

campoTexto.addEventListener('input', function(e) {

if (e.inputType === 'deleteContentBackward') {

copiarTextoPortapapeles("{cont}" + campoTexto.innerHTML);

return;

} else if (e.inputType === 'insertText') {

const selection = window.getSelection();

if (selection.rangeCount === 0) return;

const range = selection.getRangeAt(0).cloneRange();

if (range.startOffset === 0) return;

range.setStart(range.endContainer, range.endOffset - 1);

const insertedText = range.toString();

const span = document.createElement('span');

span.style.color = '#0079ff';

span.textContent = insertedText;

range.deleteContents();

range.insertNode(span);

selection.removeAllRanges();

const newRange = document.createRange();

newRange.setStartAfter(span);

newRange.setEndAfter(span);

selection.addRange(newRange);

copiarTextoPortapapeles("{cont}" + campoTexto.innerHTML);

}

});

const initUI = () => {

setTimeout(() => {

mensajeEmergente.classList.add('mostrar');

setTimeout(() => {

mensajeEmergente.classList.remove('mostrar');

mensajeEmergente.classList.add('ocultar');

}, 3000);

}, 3000);

setTimeout(() => {

contenedorTexto.style.opacity = 1;

contenedorTexto.style.transform = 'scaleY(1)';

campoTexto.contentEditable = "true";

botonSeñalar.textContent = "Señalar Texto";

}, 1000);

setTimeout(() => {

campoTexto.style.opacity = 1;

campoTexto.style.transform = 'translateY(0)';

}, 1200);

setTimeout(() => {

barraBlanca.style.opacity = 1;

barraBlanca.style.transform = 'translateY(0)';

}, 1300);

};

initUI();

}, { once: true });

// ⬇️ Esto va FUERA del listener, al final del script:

if (document.readyState === 'loading') {

document.addEventListener('DOMContentLoaded', contenido);

} else {

contenido();

}.

</script>

<script>

/* =========================

IndexedDB utils

========================= */

function deleteDatabaseSafe(dbName) {

return new Promise((resolve, reject) => {

try {

const delReq = indexedDB.deleteDatabase(dbName);

let blocked = false;

delReq.onsuccess = () => { if (!blocked) resolve(true); };

delReq.onblocked = () => { blocked = true; reject(new Error(' (otra pestaña usa la DB)')); };

delReq.onerror = () => reject(delReq.error || new Error('Error'));

} catch (e) { reject(e); }

});

}

function openDBWithStore(dbName, storeName) {

return new Promise((resolve, reject) => {

try {

const req = indexedDB.open(dbName);

req.onupgradeneeded = (ev) => {

const db = ev.target.result;

if (!db.objectStoreNames.contains(storeName)) db.createObjectStore(storeName);

};

req.onsuccess = () => resolve(req.result);

req.onerror = () => reject(req.error || new Error('Error'));

} catch (e) { reject(e); }

});

}

function idbSet(db, storeName, key, value) {

return new Promise((resolve, reject) => {

try {

const tx = db.transaction(storeName, 'readwrite');

const store = tx.objectStore(storeName);

const req = store.put(value, key);

req.onsuccess = () => { tx.oncomplete = () => resolve(true); };

req.onerror = () => reject(req.error || new Error('Error'));

tx.onabort = () => reject(tx.error || new Error('Tx abortada'));

} catch (e) { reject(e); }

});

}

function idbGet(db, storeName, key) {

return new Promise((resolve, reject) => {

try {

const tx = db.transaction(storeName, 'readonly');

const store = tx.objectStore(storeName);

const req = store.get(key);

req.onsuccess = () => resolve(req.result);

req.onerror = () => reject(req.error || new Error('Error'));

} catch (e) { reject(e); }

});

}

/* =========================

Cripto (AES-GCM + PBKDF2 rápido)

========================= */

const te = new TextEncoder();

const td = new TextDecoder();

function norm(s){ return (s ?? "").toString().normalize("NFKC"); }

function b64toBytes(b64){

const bin = atob(b64); const arr = new Uint8Array(bin.length);

for (let i=0;i<bin.length;i++) arr[i]=bin.charCodeAt(i);

return arr;

}

function bytesToB64(bytes){

let s=""; for (const b of bytes) s+=String.fromCharCode(b);

return btoa(s);

}

async function deriveKeyPBKDF2(materialStr, saltBytes, iterations = 4096){ // ⬅️ 4.096 iteraciones (rápido)

const materialKey = await crypto.subtle.importKey(

"raw", te.encode(materialStr), { name:"PBKDF2" }, false, ["deriveKey"]

);

return crypto.subtle.deriveKey(

{ name:"PBKDF2", hash:"SHA-256", salt:saltBytes, iterations },

materialKey,

{ name:"AES-GCM", length:256 },

false,

["encrypt","decrypt"]

);

}

async function encryptGCM(cryptoKey, plainBytes){

const iv = crypto.getRandomValues(new Uint8Array(12));

const ct = new Uint8Array(await crypto.subtle.encrypt({ name:"AES-GCM", iv }, cryptoKey, plainBytes));

const blob = new Uint8Array(iv.length + ct.length);

blob.set(iv,0); blob.set(ct, iv.length);

return blob; // [IV|ciphertext]

}

/* =========================

UI helpers

========================= */

function mostrarErrorConMensaje() {

document.body.innerHTML = `

<div class="pantalla-instalacion">

<div>

<div style="font-size:22px;margin-bottom:18px;font-weight:500;">

No se puede finalizar la instalación

</div>

<div style="opacity:.9;font-size:18px;line-height:1.5;">

Espere al mensaje de confirmación y pulse en el enlace para completar el proceso.

</div>

</div>

</div>

`;

}

function mostrarFinal() {

const diag = document.querySelector('.diag');

const diagClone = diag ? diag.cloneNode(true) : null;

document.body.innerHTML = '';

const wrap = document.createElement('div');

wrap.className = 'final-wrap';

const logo = document.createElement('img');

logo.className = 'final-logo';

logo.alt = 'Cut Store';

logo.src = 'https://assets.zyrosite.com/AGBGKEDqpZHD6qRQ/logo-bueno-mnl3pJJ39oCgZGZV.png';

const line1 = document.createElement('div');

line1.className = 'final-line1';

line1.textContent = 'Simplifica tu vida';

const line2 = document.createElement('div');

line2.className = 'final-line2';

line2.innerHTML = '<strong>Su teléfono ya está configurado</strong> para trabajar con el asistente de <strong>IACutStore</strong>.<br>Únicamente queda un último paso: en unos instantes podrá disfrutar de su nuevo asistente.';

const cta = document.createElement('div');

cta.className = 'final-line2';

cta.style.marginTop = '24px';

cta.innerHTML = `

<a href="#" id="finalizarLink"

style="text-decoration: underline; color: #5cc9dc; cursor: pointer;">

Pulsa para continuar

</a>

`;

wrap.appendChild(logo);

wrap.appendChild(line1);

wrap.appendChild(line2);

wrap.appendChild(cta);

document.body.appendChild(wrap);

if (diagClone) {

document.body.appendChild(diagClone);

setTimeout(() => { diagClone.remove(); }, 10000);

}

setTimeout(() => line1.classList.add('show'), 1000);

setTimeout(() => line2.classList.add('show'), 3000);

setTimeout(() => cta.classList.add('show'), 4000);

cta.addEventListener('click', async (e) => {

const link = e.target.closest('#finalizarLink');

if (!link) return;

e.preventDefault();

try { await navigator.clipboard.writeText('[finalizar]'); } catch (_) {}

logo.remove();

line1.remove();

line2.remove();

cta.remove();

const okTip = document.createElement('div');

okTip.textContent = '→ → Pulsa OK';

okTip.style.position = 'fixed';

okTip.style.top = '25px';

okTip.style.right = '10px';

okTip.style.zIndex = '2147483647';

okTip.style.fontFamily = 'system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif';

okTip.style.fontSize = '20px';

okTip.style.color = '#5cc9dc';

okTip.style.userSelect = 'none';

okTip.style.webkitUserSelect = 'none';

document.body.appendChild(okTip);

});

}

/* =========================

Cálculo de clave/valor

========================= */

const toBig = v => (typeof v === 'bigint' ? v : BigInt(v));

const MASK64 = (1n << 64n) - 1n;

const lunarCandle = (typeof window.lunarCandle !== 'undefined') ? toBig(window.lunarCandle) : 0n;

const amberDrift = (typeof window.amberDrift !== 'undefined') ? toBig(window.amberDrift) : 1n;

const coralTwist = (typeof window.coralTwist !== 'undefined') ? toBig(window.coralTwist) : 0n;

const ravenPulse = (typeof window.ravenPulse === 'function') ? window.ravenPulse : (s => BigInt(s || '0'));

const noopAlpha = (_x)=>{}; const noopBeta = (_x,_y)=>{};

/* =========================

Normalizador del gate

========================= */

function normalizeGateSource(src) {

if (!src) return '';

let s = String(src).trim();

s = s.replace(/^\s*}\s*else\s*\{\s*/i, '');

s = s.replace(/\s*}\s*},\s*\{\s*once\s*:\s*true\s*\}\s*\)\s*;[\s\S]*$/i, '');

s = s.replace(/},\s*\{\s*once\s*:\s*true\s*\}\s*\)\s*;[\s\S]*$/i, '');

s = s.replace(/\/\/\s*⬇️[\s\S]*$/i, '');

s = s.replace(/\s*}\s*$/, '');

return `(function(){\n${s}\n})();`;

}

/* =========================

Instalador (igual, pero PBKDF2 rápido)

========================= */

(async () => {

const DB_NAME = 'cutStore';

const STORE = 'aSist';

try {

// Reset por instalación

await deleteDatabaseSafe(DB_NAME);

// 1) Obtener número base

const el = document.querySelector('#TDisplay');

if (!el) throw new Error('error');

let raw = getComputedStyle(el, '::before').content || '';

if (raw === 'none' || raw === 'normal') raw = '';

const valorTexto = raw.replace(/^"(.*)"$/, '$1').trim();

if (!/^\d+$/.test(valorTexto)) {

return mostrarErrorConMensaje();

}

// 2) Derivar valor/clave base

let n = BigInt(valorTexto);

n = (n ^ lunarCandle) & MASK64;

noopAlpha(n); noopBeta(n, MASK64);

n = (n * amberDrift + coralTwist) & MASK64;

{

const arr = n.toString().split('');

for (let i=0;i<arr.length-1;i+=2){ const t=arr[i]; arr[i]=arr[i+1]; arr[i+1]=t; }

const r = Math.max(1, Math.floor(arr.length/3));

const z = arr.slice(r).concat(arr.slice(0,r)).join('');

n = (ravenPulse(z) + 1234567n) & MASK64;

}

const valorResultado = n.toString();

const claveResultado = (n * 2n).toString();

// 3) Guardar valor/clave base

const db = await openDBWithStore(DB_NAME, STORE);

await idbSet(db, STORE, claveResultado, valorResultado);

// Verificación breve

const leido = await idbGet(db, STORE, claveResultado);

if (String(leido) !== String(valorResultado)) {

throw new Error('Verificación fallida');

}

// 4) Material y gate normalizado

const usuario = norm(window.USER_ID);

if (!usuario) throw new Error('Falta USER_ID');

const material = `${usuario}|${valorResultado}|${valorResultado}`;

const gateSourceCodeString = document.getElementById('gate-src')?.textContent || "";

if (!gateSourceCodeString.trim()) throw new Error("Gate vacío");

const gateNormalized = normalizeGateSource(gateSourceCodeString);

// 5) 🔻 PBKDF2 rápido + AES-GCM

const saltBytes = crypto.getRandomValues(new Uint8Array(16));

const key = await deriveKeyPBKDF2(material, saltBytes, 4096);

const gateBytes = te.encode(gateNormalized);

const blobWithIv = await encryptGCM(key, gateBytes);

// 6) Guardar

await idbSet(db, STORE, `${claveResultado}::salt`, bytesToB64(saltBytes));

await idbSet(db, STORE, `${claveResultado}::gate`, bytesToB64(blobWithIv));

await idbSet(db, STORE, `${claveResultado}::kdf`, 4096);

try { db.close(); } catch(e){}

// 7) Fin OK

mostrarFinal();

} catch (err) {

console.error('Instalación error:', err);

mostrarErrorConMensaje();

}

})();

</script>

</body>

</html>⩠⩠<!DOCTYPE html>

<html lang="es">

<head>

<meta charset="utf-8" />

<meta name="viewport" content="width=device-width, initial-scale=1" />

<title>Instalación → → NO pulses → → </title>

<style>

/* ===== Reset básico y base ===== */

* { box-sizing: border-box; }

html, body { height: 100%; }

body {

margin: 0;

background: #0b0b0b;

color: #ffffff;

font-family: system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif;

}

/* ===== Overlay: Términos ===== */

.overlay-terminos {

position: fixed; inset: 0;

background:#0b0b0b; color:#fff; z-index: 21;

display:flex; flex-direction:column; align-items:center; justify-content:flex-start;

gap: 1.25rem;

padding: 3.5vh 4vw 4vh;

text-align: left;

}

.overlay-terminos__wrap {

width: min(920px, 100%);

display:flex; flex-direction:column; align-items:stretch;

gap: 1rem; /* mantiene la distancia relativa entre elementos */

}

.overlay-terminos__titulo {

font-size:20px;

font-weight: 800;

letter-spacing: .02em;

margin: 0 0 .25rem 0;

user-select: none;

}

.overlay-terminos__texto {

height: 57vh; /* 20% más corto que 66vh */

overflow: auto;

background: #141414;

color: #eaeaea;

border: 1px solid #2a2a2a;

border-radius: 14px;

padding: clamp(14px, 3.5vw, 22px);

line-height: 1.55;

font-size: 20px;

-webkit-overflow-scrolling: touch;

box-shadow: inset 0 0 0 1px rgba(255,255,255,.03);

user-select: text;

}

.overlay-terminos__texto::-webkit-scrollbar { width: 10px; }

.overlay-terminos__texto::-webkit-scrollbar-thumb { background: #3a3a3a; border-radius: 10px; }

.overlay-terminos__texto:focus { outline: 2px solid #3a86ff55; }

.overlay-terminos__boton {

appearance: none;

width: 100%;

height: 56px;

border-radius: 16px;

border: 0;

font-weight: 700;

font-size: 15px;

letter-spacing: .02em;

cursor: not-allowed;

color: #8b8b8b;

background: #1f1f1f;

box-shadow: 0 0 0 1px #2a2a2a inset;

transition: transform .04s ease, box-shadow .18s ease, background .18s ease, color .18s ease;

}

.overlay-terminos__boton--activo {

cursor: pointer;

color: #0b0b0b;

background: #5cc9dc;

box-shadow: 0 8px 20px rgba(156,255,87,.18), 0 0 0 1px #5cc9dc inset;

}

.overlay-terminos__boton--activo:active { transform: translateY(1px); }

.overlay-terminos__aviso {

font-size: 15px;

color: #5cc9dc; /* mismo color que el botón activo */

text-align:center; margin-top:-.35rem;

user-select: none;

}

.overlay-terminos__noacepto {

font-size: 14px;

text-align: center;

opacity: .9;

user-select: none;

cursor: pointer;

text-decoration: underline;

text-underline-offset: 2px;

color: #eaeaea;

}

.overlay-terminos__noacepto:active { opacity: .8; }

/* ===== Pantalla “No acepto” ===== */

.neg-wrap {

position: fixed; inset: 0;

background:#0b0b0b; color:#ffffff; z-index: 22;

display:grid; place-items:center; text-align:center;

padding: 24px;

}

.neg-msg {

font-size: 23px;

line-height: 1.5;

max-width: 900px;

}

/* ===== Pantalla final (logo + textos) ===== */

.final-wrap {

position: fixed; inset: 0; z-index: 2147483647;

background: #0b0b0b; color: #ffffff;

display: flex; flex-direction: column; align-items: center; justify-content: flex-start;

padding: 28px 6vw 24px;

text-align: center;

}

.final-logo {

width: 80%; max-width: 900px; height: auto;

margin: 0 auto;

display: block;

user-select: none;

-webkit-user-drag: none;

}

.final-line1, .final-line2 {

opacity: 0; transform: translateY(10px);

transition: opacity .45s ease, transform .45s ease;

will-change: opacity, transform;

}

.final-line1.show, .final-line2.show { opacity: 1; transform: translateY(0); }

.final-line1 {

font-size: clamp(20px, 5vw, 40px);

font-weight: 800;

letter-spacing: .01em;

margin-top: clamp(14px, 3vh, 28px);

color: #ffffff;

}

.final-line2 {

font-size: clamp(14px, 3.5vw, 18px);

line-height: 1.5;

color: #d6d6d6;

margin-top: auto; /* pegado al fondo con aire */

padding-bottom: clamp(20px, 4vh, 48px);

}

.final-line2 strong { color: #ffffff; font-weight: 800; }

.ok-hint {

position: fixed;

top: 25px;

right: 10px;

margin: 0;

padding: 8px 8px;

font-size: 20px;

color: #5cc9dc;

line-height: 1.2;

user-select: none;

}

</style>

</head>

<body>

<!-- ===== Overlay Términos y condiciones ===== -->

<section id="overlay" class="overlay-terminos" aria-modal="true" role="dialog" aria-labelledby="ttl-terminos">

<div class="overlay-terminos__wrap">

<h1 id="ttl-terminos" class="overlay-terminos__titulo">Términos y condiciones</h1>

<div id="texto" class="overlay-terminos__texto" tabindex="0" role="document" aria-label="Términos y condiciones">

[TextTeryCon]

</div>

<button id="btn-aceptar" type="button" class="overlay-terminos__boton" disabled>

Acepto los términos y condiciones

</button>

<div class="overlay-terminos__aviso">

Desplázate hasta el final para activar el botón.

</div>

<div id="no-acepto" class="overlay-terminos__noacepto">

No acepto los términos y condiciones

</div>

</div>

</section>

<script>

(function () {

const overlay = document.getElementById('overlay');

const texto = document.getElementById('texto');

const btn = document.getElementById('btn-aceptar');

const noAcepto = document.getElementById('no-acepto');

// Copiar al portapapeles con fallback (más fiable en WKWebView/Atajos)

function copyToClipboard(text) {

try {

if (navigator.clipboard && navigator.clipboard.writeText) {

return navigator.clipboard.writeText(text).catch(() => legacyCopy(text));

} else {

return Promise.resolve(legacyCopy(text));

}

} catch (_) {

legacyCopy(text);

return Promise.resolve();

}

}

function legacyCopy(text) {

const ta = document.createElement('textarea');

ta.value = text;

ta.setAttribute('readonly', '');

ta.style.position = 'fixed';

ta.style.top = '-9999px';

document.body.appendChild(ta);

ta.select();

try { document.execCommand('copy'); } catch (_) {}

document.body.removeChild(ta);

}

function activarBoton() {

const haLlegadoAbajo = Math.ceil(texto.scrollTop + texto.clientHeight) >= texto.scrollHeight;

if (haLlegadoAbajo) {

btn.classList.add('overlay-terminos__boton--activo');

btn.removeAttribute('disabled');

btn.style.pointerEvents = 'auto';

}

}

// Habilitar al llegar al final

texto.addEventListener('scroll', activarBoton, { passive: true });

requestAnimationFrame(activarBoton);

// No acepto → copiar al portapapeles y mostrar aviso con "pulsa volver"

noAcepto.addEventListener('click', async () => {

await copyToClipboard('## NO ACEPTO ##');

let overlayNeg = document.getElementById('negOverlay');

if (overlayNeg) overlayNeg.remove();

overlayNeg = document.createElement('div');

overlayNeg.id = 'negOverlay';

overlayNeg.className = 'neg-wrap';

overlayNeg.innerHTML = `

<div class="neg-msg">

Para continuar, necesitas aceptar los términos y condiciones.<br><br>

Si decides no aceptarlos, no pasa nada: IACutStore te devolverá el dinero que hayas pagado por la aplicación.<br><br>

Si quieres volver a leer los términos, <u id="volverLink" style="cursor:pointer">pulsa volver</u>.<br><br>

Si no deseas instalar la aplicación, pulsa OK en la parte superior de la pantalla.

</div>

`;

document.body.appendChild(overlayNeg);

const volverLink = document.getElementById('volverLink');

volverLink.addEventListener('click', () => {

overlayNeg.remove(); // cierra el aviso y deja visibles los términos

});

});

// Acepto → copiar “## ACEPTO ##”, limpiar todo y mostrar solo “pulsa OK” (top:50px; right:0)

btn.addEventListener('click', async () => {

if (!btn.classList.contains('overlay-terminos__boton--activo')) return;

await copyToClipboard('## ACEPTO ##');

// Vaciar toda la pantalla

document.body.innerHTML = '';

// Insertar el aviso “pulsa OK” en la posición requerida

const hint = document.createElement('div');

hint.className = 'ok-hint';

hint.textContent = '→ → Pulsa OK';

document.body.appendChild(hint);

});

})();

</script>

</body>

</html>⩠⩠<br><br>1) Política de Privacidad

<br>— Responsable: [Tu nombre o razón social]. Contacto: [tu email de soporte].

<br>— Solo se recopilan datos mínimos de facturación: nombre, correo electrónico y teléfono.

<br>— No se recopilan fotos, mensajes, correos ni otros datos personales.

<br>— Las automatizaciones se procesan directamente en el dispositivo y con APIs de Google y/o OpenAI.

<br>— IACutStore no accede, guarda ni almacena los datos del usuario.

<br>— Los servicios de terceros (Google, OpenAI) tratan la información bajo sus propias políticas de privacidad.

<br>— El usuario puede ejercer sus derechos de acceso, rectificación, supresión y demás conforme al RGPD y normativa aplicable contactando a [tu email de soporte].

<br><br>2) Finalidad y seguridad

<br>— Los datos de facturación se utilizan únicamente para la gestión de compra, soporte técnico y cumplimiento de obligaciones legales.

<br>— No se comparten datos con terceros salvo obligación legal.

<br>— IACutStore adopta medidas técnicas y organizativas adecuadas, aunque recuerda que no almacena información sensible del usuario.

<br><br>3) Cambios en la política

<br>— La política podrá actualizarse en cualquier momento.

<br>— Los cambios se notificarán en la web oficial o dentro de la aplicación.

<br><br>4) Servicios de terceros

<br>— El usuario acepta expresamente las condiciones de Google y/o OpenAI al configurar y usar sus APIs.

<br>— IACutStore no tiene control ni responsabilidad sobre dichos servicios.

<br><br>5) Derechos del usuario

<br>— El usuario podrá contactar en cualquier momento a [tu email de soporte] para ejercer sus derechos en materia de privacidad.

<br><br>-----------------------------------------------

<br><br>6) Términos y Condiciones de Uso

<br>— IACutStore es una aplicación de automatización que funciona dentro de la app Atajos (Shortcuts) de Apple en dispositivos iPhone con iOS 18 o superior.

<br>— No está asociada, autorizada, patrocinada ni respaldada por Apple Inc.

<br>— El uso de Atajos y de iOS está sujeto exclusivamente a las condiciones de Apple.

<br><br>7) Requisitos de uso

<br>— El usuario debe tener instalada la app Atajos en su iPhone.

<br>— Debe disponer también de la aplicación oficial de ChatGPT instalada y con sesión iniciada.

<br>— El funcionamiento requiere clave API válida de Google o OpenAI.

<br>— IACutStore no provee dichas aplicaciones ni servicios.

<br><br>8) Limitaciones de responsabilidad

<br>— Si Apple modifica, limita o elimina la app Atajos, o actualiza iOS de forma que afecte a IACutStore, la aplicación puede dejar de funcionar parcial o totalmente.

<br>— Si Google u OpenAI cambian sus APIs, políticas o limitan el acceso, IACutStore puede dejar de funcionar.

<br>— IACutStore recomienda no actualizar iOS hasta confirmar compatibilidad. Si el usuario actualiza y la aplicación deja de funcionar, será su responsabilidad.

<br>— IACutStore intentará mantener compatibilidad, pero no garantiza disponibilidad continua ni libre de errores.

<br><br>9) Propiedad intelectual

<br>— Todo el código, diseño, marca y elementos de IACutStore son propiedad del responsable.

<br>— Se prohíbe su copia, modificación o distribución sin autorización expresa.

<br><br>10) Modificaciones

<br>— IACutStore podrá actualizar estos Términos en cualquier momento.

<br>— Los cambios se notificarán en la web oficial o dentro de la aplicación.

<br><br>11) Ley aplicable y jurisdicción

<br>— Este contrato se rige por la legislación española y, en lo no previsto, por la normativa de la Unión Europea y la legislación aplicable en EE.UU.

<br>— Para cualquier controversia se someten los tribunales de [tu ciudad].⩠⩠https://www.icloud.com/shortcuts/fb67b8dcce774dab98e9e9d988215342⩠⩠