Size
8.6 KB
Version
1.0.1
Created
Feb 6, 2026
Updated
29 days ago
1// ==UserScript==
2// @name Extension for lovable.dev
3// @description A new extension
4// @version 1.0.1
5// @match https://*.lovable.dev/*
6// @icon https://lovable.dev/favicon.svg
7// ==/UserScript==
8(function() {
9 'use strict';
10
11 console.log('Extensão Ler Carta iniciada');
12
13 // Função para adicionar botão de ler carta
14 function addReadCardButton(cardElement) {
15 // Verificar se já tem o botão
16 if (cardElement.querySelector('.read-card-button')) {
17 return;
18 }
19
20 // Criar o botão
21 const button = document.createElement('button');
22 button.textContent = 'Ler Carta';
23 button.className = 'read-card-button';
24 button.style.cssText = `
25 position: absolute;
26 bottom: 10px;
27 left: 50%;
28 transform: translateX(-50%);
29 background-color: #4CAF50;
30 color: white;
31 border: none;
32 padding: 8px 16px;
33 border-radius: 4px;
34 cursor: pointer;
35 font-size: 14px;
36 font-weight: bold;
37 z-index: 1000;
38 box-shadow: 0 2px 4px rgba(0,0,0,0.2);
39 `;
40
41 button.addEventListener('mouseenter', () => {
42 button.style.backgroundColor = '#45a049';
43 });
44
45 button.addEventListener('mouseleave', () => {
46 button.style.backgroundColor = '#4CAF50';
47 });
48
49 button.addEventListener('click', async (e) => {
50 e.stopPropagation();
51
52 // Pegar a imagem da carta
53 const cardImage = cardElement.querySelector('img[src*="card"], img[alt*="card"]');
54 if (!cardImage) {
55 console.error('Imagem da carta não encontrada');
56 return;
57 }
58
59 button.textContent = 'Lendo...';
60 button.disabled = true;
61
62 try {
63 // Usar RM.aiCall para ler a carta
64 const cardDescription = await RM.aiCall(
65 `Analise esta carta de jogo e descreva seus atributos, habilidades e efeitos em português. URL da imagem: ${cardImage.src}`,
66 {
67 type: 'json_schema',
68 json_schema: {
69 name: 'card_reading',
70 schema: {
71 type: 'object',
72 properties: {
73 nome: { type: 'string' },
74 tipo: { type: 'string' },
75 atributos: { type: 'array', items: { type: 'string' } },
76 habilidades: { type: 'array', items: { type: 'string' } },
77 descricao: { type: 'string' }
78 },
79 required: ['nome', 'descricao']
80 }
81 }
82 }
83 );
84
85 // Mostrar resultado
86 showCardInfo(cardDescription, cardElement);
87
88 } catch (error) {
89 console.error('Erro ao ler carta:', error);
90 alert('Erro ao ler a carta. Tente novamente.');
91 } finally {
92 button.textContent = 'Ler Carta';
93 button.disabled = false;
94 }
95 });
96
97 // Adicionar o botão à carta
98 cardElement.style.position = 'relative';
99 cardElement.appendChild(button);
100 console.log('Botão adicionado à carta');
101 }
102
103 // Função para mostrar informações da carta
104 function showCardInfo(cardInfo, cardElement) {
105 // Remover info anterior se existir
106 const existingInfo = cardElement.querySelector('.card-info-display');
107 if (existingInfo) {
108 existingInfo.remove();
109 }
110
111 // Criar elemento de informação
112 const infoDiv = document.createElement('div');
113 infoDiv.className = 'card-info-display';
114 infoDiv.style.cssText = `
115 position: absolute;
116 top: 0;
117 left: 0;
118 right: 0;
119 bottom: 0;
120 background-color: rgba(0, 0, 0, 0.9);
121 color: white;
122 padding: 20px;
123 border-radius: 8px;
124 overflow-y: auto;
125 z-index: 999;
126 `;
127
128 infoDiv.innerHTML = `
129 <button class="close-info" style="position: absolute; top: 10px; right: 10px; background: #f44336; color: white; border: none; padding: 5px 10px; border-radius: 4px; cursor: pointer;">✕</button>
130 <h3 style="margin-top: 0;">${cardInfo.nome}</h3>
131 <p><strong>Tipo:</strong> ${cardInfo.tipo || 'N/A'}</p>
132 ${cardInfo.atributos && cardInfo.atributos.length > 0 ? `<p><strong>Atributos:</strong> ${cardInfo.atributos.join(', ')}</p>` : ''}
133 ${cardInfo.habilidades && cardInfo.habilidades.length > 0 ? `<p><strong>Habilidades:</strong></p><ul>${cardInfo.habilidades.map(h => `<li>${h}</li>`).join('')}</ul>` : ''}
134 <p><strong>Descrição:</strong> ${cardInfo.descricao}</p>
135 `;
136
137 // Adicionar evento de fechar
138 infoDiv.querySelector('.close-info').addEventListener('click', (e) => {
139 e.stopPropagation();
140 infoDiv.remove();
141 });
142
143 cardElement.appendChild(infoDiv);
144 }
145
146 // Função para encontrar cartas na página
147 function findCards() {
148 const cards = new Set();
149
150 // Procurar apenas por imagens que claramente são cartas de jogo
151 const allImages = document.querySelectorAll('img');
152
153 allImages.forEach(img => {
154 // Verificar se a imagem parece ser uma carta
155 const src = img.src || '';
156 const alt = img.alt || '';
157 const className = img.className || '';
158
159 // Critérios muito específicos para identificar cartas
160 const looksLikeCard = (
161 (src.includes('card') || alt.includes('card') || alt.includes('carta')) &&
162 !src.includes('icon') &&
163 !src.includes('logo') &&
164 !src.includes('avatar') &&
165 !className.includes('icon') &&
166 !className.includes('logo')
167 );
168
169 if (!looksLikeCard) {
170 return;
171 }
172
173 // Verificar tamanho da imagem (cartas geralmente são maiores)
174 const rect = img.getBoundingClientRect();
175 const isCardSize = rect.width >= 100 && rect.height >= 140 && rect.width <= 500 && rect.height <= 700;
176
177 if (!isCardSize) {
178 return;
179 }
180
181 // Pegar o container pai apropriado
182 let cardElement = img.parentElement;
183
184 // Verificar se o pai não é um botão ou link genérico
185 if (cardElement && (cardElement.tagName === 'BUTTON' || cardElement.tagName === 'A')) {
186 cardElement = cardElement.parentElement;
187 }
188
189 if (cardElement) {
190 cards.add(cardElement);
191 }
192 });
193
194 console.log(`Encontradas ${cards.size} cartas`);
195 return Array.from(cards);
196 }
197
198 // Função para processar cartas
199 function processCards() {
200 const cards = findCards();
201 cards.forEach(card => {
202 addReadCardButton(card);
203 });
204 }
205
206 // Função de debounce
207 function debounce(func, wait) {
208 let timeout;
209 return function executedFunction(...args) {
210 const later = () => {
211 clearTimeout(timeout);
212 func(...args);
213 };
214 clearTimeout(timeout);
215 timeout = setTimeout(later, wait);
216 };
217 }
218
219 // Observar mudanças no DOM
220 const debouncedProcess = debounce(processCards, 1000);
221
222 const observer = new MutationObserver(debouncedProcess);
223
224 // Inicializar quando o body estiver pronto
225 function init() {
226 if (document.body) {
227 console.log('Iniciando observação do DOM');
228
229 // Processar cartas existentes
230 processCards();
231
232 // Observar mudanças
233 observer.observe(document.body, {
234 childList: true,
235 subtree: true
236 });
237 } else {
238 setTimeout(init, 100);
239 }
240 }
241
242 // Iniciar
243 if (document.readyState === 'loading') {
244 document.addEventListener('DOMContentLoaded', init);
245 } else {
246 init();
247 }
248
249})();