Greasy Fork is available in English.
Versión temporal exclusiva para DeepZer Flows: conserva el mensaje completo y muestra la burbuja enviada como Fix error.
// ==UserScript==
// @name DeepZer Flows Compact Sender TEMP
// @namespace https://deepzerr.com/lovable-compact-sender/deepzer-flows
// @version 1.0.0
// @license MIT
// @description Versión temporal exclusiva para DeepZer Flows: conserva el mensaje completo y muestra la burbuja enviada como Fix error.
// @author DeepZer
// @match https://lovable.dev/projects/cec453fc-d962-4e70-817b-a68f4bcfe8bb*
// @run-at document-start
// @noframes
// @sandbox raw
// @grant none
// ==/UserScript==
(function () {
'use strict';
const VERSION = '1.0.0';
const PROJECT_ID = 'cec453fc-d962-4e70-817b-a68f4bcfe8bb';
const STATUS_ID = 'dzf-compact-status';
const STYLE_ID = 'dzf-compact-styles';
const FORM_SELECTOR = 'form#chat-input';
const FETCH_MARKER = '__dzfCompactFetchVersion';
const BOOT_MARKER = '__dzfCompactBootVersion';
const VISUAL_TIMEOUT_MS = 12000;
const CHIP_TEXT = 'Fix error';
if (!location.pathname.includes(`/projects/${PROJECT_ID}`)) return;
if (window[BOOT_MARKER] === VERSION) return;
window[BOOT_MARKER] = VERSION;
function log(...args) {
console.log(`[DeepZer Flows Compact v${VERSION}]`, ...args);
}
function warn(...args) {
console.warn(`[DeepZer Flows Compact v${VERSION}]`, ...args);
}
function normalizeText(value) {
return String(value || '')
.replace(/\u00a0/g, ' ')
.replace(/\s+/g, ' ')
.trim();
}
function isChatRequest(input, init) {
const method = String(
init?.method ||
(input instanceof Request ? input.method : 'GET')
).toUpperCase();
if (method !== 'POST') return false;
const rawUrl =
typeof input === 'string'
? input
: input instanceof URL
? input.href
: input?.url || '';
try {
const url = new URL(rawUrl, location.href);
const lovableHost =
url.hostname === 'lovable.dev' ||
url.hostname.endsWith('.lovable.dev');
return lovableHost && url.pathname.includes('/chat');
} catch (_) {
const value = String(rawUrl);
return (
value.includes('lovable.dev') &&
value.includes('/chat')
);
}
}
function decoratePayload(payload) {
if (
!payload ||
typeof payload !== 'object' ||
typeof payload.message !== 'string' ||
!payload.message.trim()
) {
return null;
}
const originalMessage = payload.message;
const buildState = window.__lcsBuildState || {};
const fallbackId = String(payload.id || '');
const eventId = String(buildState.eventId || fallbackId);
/*
* El texto original no se modifica.
* Lovable recibe la instrucción completa.
*/
payload.intent = 'fix_error';
payload.message_intent_metadata = {
fix_error_metadata: {
errors: [
{
error_type: 'build',
error_message: String(
buildState.errorMessage || originalMessage
),
build_event_id: eventId
}
]
}
};
payload.contains_error = true;
payload.error_source = 'build_errors';
payload.error_ids = eventId ? [eventId] : [];
return {
payload,
originalMessage
};
}
function modifyStringBody(body) {
if (typeof body !== 'string') return null;
try {
const payload = JSON.parse(body);
const result = decoratePayload(payload);
if (!result) return null;
return {
body: JSON.stringify(result.payload),
originalMessage: result.originalMessage
};
} catch (_) {
return null;
}
}
function injectStyles() {
if (document.getElementById(STYLE_ID)) return;
const style = document.createElement('style');
style.id = STYLE_ID;
style.textContent = `
#${STATUS_ID} {
display: flex;
align-items: center;
gap: 7px;
width: max-content;
margin: 6px 0 0 8px;
padding: 0;
border: 0 !important;
background: transparent !important;
box-shadow: none !important;
color: #8a8a93;
font: 500 11px/1 Inter, system-ui, -apple-system,
BlinkMacSystemFont, "Segoe UI", sans-serif;
pointer-events: none;
user-select: none;
}
#${STATUS_ID} .dzf-status-dot {
width: 7px;
height: 7px;
flex: 0 0 7px;
border-radius: 999px;
background: #22c55e;
animation: dzf-status-pulse 1.45s ease-in-out infinite;
}
.dzf-fix-chip {
display: inline-flex !important;
align-items: center !important;
justify-content: center !important;
width: max-content !important;
min-width: 0 !important;
max-width: 180px !important;
min-height: 32px !important;
padding: 7px 13px !important;
border: 1px solid rgba(148, 163, 184, 0.22) !important;
border-radius: 999px !important;
background: rgba(44, 44, 46, 0.96) !important;
color: #d7d7dc !important;
font-size: 13px !important;
font-style: italic !important;
font-weight: 500 !important;
line-height: 1 !important;
white-space: nowrap !important;
overflow: hidden !important;
text-overflow: ellipsis !important;
}
@keyframes dzf-status-pulse {
0%, 100% {
opacity: 1;
box-shadow:
0 0 0 0 rgba(34, 197, 94, 0.50);
}
50% {
opacity: 0.55;
box-shadow:
0 0 0 5px rgba(34, 197, 94, 0);
}
}
`;
(
document.head ||
document.documentElement
).appendChild(style);
}
function mountStatus() {
const form = document.querySelector(FORM_SELECTOR);
if (!form?.isConnected) return false;
injectStyles();
let status = document.getElementById(STATUS_ID);
if (!status) {
status = document.createElement('div');
status.id = STATUS_ID;
}
status.title =
`DeepZer Flows Compact Sender v${VERSION} activo`;
status.innerHTML = `
<span
class="dzf-status-dot"
aria-hidden="true"
></span>
<span>En uso · Flows TEMP v${VERSION}</span>
`;
if (status.previousElementSibling !== form) {
form.insertAdjacentElement('afterend', status);
}
return true;
}
function mountStatusWithRetries(attempt = 0) {
if (mountStatus() || attempt >= 20) return;
window.setTimeout(
() => mountStatusWithRetries(attempt + 1),
400
);
}
function isVisible(element) {
if (!(element instanceof Element)) return false;
const rect = element.getBoundingClientRect();
const style = getComputedStyle(element);
return (
rect.width > 0 &&
rect.height > 0 &&
style.display !== 'none' &&
style.visibility !== 'hidden'
);
}
function messageMatches(text, originalMessage) {
const candidate = normalizeText(text);
const original = normalizeText(originalMessage);
if (!candidate || !original) return false;
if (candidate === original) return true;
const prefix = original.slice(
0,
Math.min(100, original.length)
);
const suffix = original.slice(
Math.max(0, original.length - 60)
);
return (
candidate.includes(prefix) &&
(
original.length < 120 ||
!suffix ||
candidate.includes(suffix)
)
);
}
function findNewestUserMessage(originalMessage) {
const containers = Array.from(
document.querySelectorAll('div[data-message-id]')
).filter((container) => {
if (!isVisible(container)) return false;
const userGroup =
container.querySelector('.group\\/user-message');
if (!userGroup) return false;
return messageMatches(
container.textContent,
originalMessage
);
});
if (!containers.length) return null;
return containers.reduce((latest, current) => {
if (!latest) return current;
const latestRect =
latest.getBoundingClientRect();
const currentRect =
current.getBoundingClientRect();
return currentRect.bottom >= latestRect.bottom
? current
: latest;
}, null);
}
function findBubbleInside(container, originalMessage) {
if (!(container instanceof Element)) return null;
const nativeChip =
container.querySelector('div.special-message');
if (nativeChip) return nativeChip;
const preferred = Array.from(
container.querySelectorAll(
'div[class*="bg-secondary-pulse"], ' +
'div[class*="whitespace-pre-wrap"], ' +
'div[class*="rounded-br-1"], ' +
'div[class*="max-w-[300px]"]'
)
).filter((element) => {
if (!isVisible(element)) return false;
if (element.closest(FORM_SELECTOR)) return false;
return messageMatches(
element.textContent,
originalMessage
);
});
if (preferred.length) {
return preferred.reduce((best, current) => {
if (!best) return current;
return current.contains(best)
? current
: best;
}, null);
}
const all = Array.from(
container.querySelectorAll('div, p, span')
)
.filter((element) => {
if (!isVisible(element)) return false;
if (element.closest(FORM_SELECTOR)) return false;
return messageMatches(
element.textContent,
originalMessage
);
})
.sort(
(a, b) =>
b.querySelectorAll('*').length -
a.querySelectorAll('*').length
);
return all[0] || null;
}
function convertToFixChip(target) {
if (!(target instanceof Element)) return false;
const chip =
target.matches('div.special-message')
? target
: (() => {
target.replaceChildren();
const element =
document.createElement('div');
element.className =
'special-message dzf-fix-chip';
target.appendChild(element);
return element;
})();
chip.textContent = CHIP_TEXT;
chip.classList.add('dzf-fix-chip');
chip.dataset.dzfCompact = VERSION;
chip.setAttribute('aria-label', CHIP_TEXT);
chip.title = CHIP_TEXT;
return true;
}
function compactOutgoingMessage(originalMessage) {
injectStyles();
const startedAt = Date.now();
let observer = null;
let stopped = false;
const stop = () => {
if (stopped) return;
stopped = true;
observer?.disconnect();
};
const inspect = () => {
if (stopped) return true;
const container =
findNewestUserMessage(originalMessage);
if (container) {
const bubble =
findBubbleInside(
container,
originalMessage
);
if (
bubble &&
convertToFixChip(bubble)
) {
log(
'Burbuja de DeepZer Flows convertida a Fix error.'
);
stop();
return true;
}
}
if (
Date.now() - startedAt >=
VISUAL_TIMEOUT_MS
) {
warn(
'No se encontró la nueva burbuja dentro del tiempo de búsqueda.'
);
stop();
}
return false;
};
const begin = () => {
if (
stopped ||
!document.body
) {
return;
}
observer = new MutationObserver(() => {
inspect();
});
observer.observe(
document.body,
{
childList: true,
subtree: true,
characterData: true
}
);
[
0,
60,
120,
240,
450,
800,
1300,
2100,
3500,
5500,
8000,
11000
].forEach((delay) => {
window.setTimeout(inspect, delay);
});
window.setTimeout(
stop,
VISUAL_TIMEOUT_MS + 300
);
};
if (document.body) {
begin();
} else {
document.addEventListener(
'DOMContentLoaded',
begin,
{
once: true
}
);
}
}
function installFetchInterceptor() {
const currentFetch = window.fetch;
if (typeof currentFetch !== 'function') {
return false;
}
if (
currentFetch[FETCH_MARKER] === VERSION
) {
return true;
}
async function dzfFetch(input, init) {
if (!isChatRequest(input, init)) {
return currentFetch.call(
window,
input,
init
);
}
try {
const modified =
modifyStringBody(init?.body);
if (modified) {
/*
* La búsqueda visual empieza antes de
* entregar la solicitud a Lovable.
*/
compactOutgoingMessage(
modified.originalMessage
);
return currentFetch.call(
window,
input,
{
...init,
body: modified.body
}
);
}
if (input instanceof Request) {
const rawBody =
await input.clone().text();
const requestModified =
modifyStringBody(rawBody);
if (requestModified) {
compactOutgoingMessage(
requestModified.originalMessage
);
const nextRequest =
new Request(input, {
body: requestModified.body
});
return currentFetch.call(
window,
nextRequest,
init
);
}
}
} catch (error) {
warn(
'Se mantuvo el envío original por seguridad.',
error
);
}
return currentFetch.call(
window,
input,
init
);
}
Object.defineProperty(
dzfFetch,
FETCH_MARKER,
{
value: VERSION,
configurable: false,
enumerable: false,
writable: false
}
);
window.fetch = dzfFetch;
log(
'Interceptor temporal instalado para DeepZer Flows.'
);
return (
window.fetch[FETCH_MARKER] === VERSION
);
}
installFetchInterceptor();
window.setTimeout(
installFetchInterceptor,
1200
);
window.setTimeout(
installFetchInterceptor,
3500
);
if (
document.readyState === 'loading'
) {
document.addEventListener(
'DOMContentLoaded',
mountStatusWithRetries,
{
once: true
}
);
} else {
mountStatusWithRetries();
}
document.addEventListener(
'focusin',
(event) => {
if (
event.target?.closest?.(
FORM_SELECTOR
)
) {
mountStatus();
}
},
true
);
})();