An AI-powered extension that automates tasks and tracks progress on Lovable platform
Size
11.5 KB
Version
1.0.3
Created
Mar 27, 2026
Updated
19 days ago
1// ==UserScript==
2// @name Lovable Task Automation Assistant
3// @description An AI-powered extension that automates tasks and tracks progress on Lovable platform
4// @version 1.0.3
5// @match https://*.lovable.dev/*
6// @match *://*/*
7// @icon https://lovable.dev/favicon.svg
8// ==/UserScript==
9(function() {
10 'use strict';
11
12 // Các biến toàn cục
13 const STATE = {
14 isActive: false,
15 currentProject: null,
16 tasks: [],
17 observer: null
18 };
19
20 // Hàm khởi tạo chính
21 function init() {
22 console.log('Lovable Task Automation Assistant initialized');
23
24 // Luôn tạo UI để hiển thị thông tin
25 setupUI();
26
27 // Kiểm tra xem chúng ta có đang ở trang Lovable không
28 if (isLovablePage()) {
29 setupObservers();
30 loadSavedState();
31 }
32 }
33
34 // Kiểm tra xem trang hiện tại có phải là trang Lovable không
35 function isLovablePage() {
36 return window.location.hostname.includes('lovable.dev');
37 }
38
39 // Thiết lập giao diện người dùng
40 function setupUI() {
41 // Tạo nút bật/tắt cho extension
42 const toggleButton = document.createElement('button');
43 toggleButton.id = 'automation-toggle';
44 toggleButton.textContent = 'Automation: OFF';
45 toggleButton.style.position = 'fixed';
46 toggleButton.style.top = '10px';
47 toggleButton.style.right = '10px';
48 toggleButton.style.zIndex = '9999';
49 toggleButton.style.padding = '10px';
50 toggleButton.style.backgroundColor = '#4CAF50';
51 toggleButton.style.color = 'white';
52 toggleButton.style.border = 'none';
53 toggleButton.style.borderRadius = '5px';
54 toggleButton.style.cursor = 'pointer';
55
56 // Chỉ thêm event listener nếu đang ở trang Lovable
57 if (isLovablePage()) {
58 toggleButton.addEventListener('click', toggleAutomation);
59 } else {
60 // Ở các trang khác, nút sẽ hiển thị trạng thái nhưng không tương tác
61 toggleButton.disabled = true;
62 toggleButton.style.opacity = '0.5';
63 toggleButton.title = 'Chỉ khả dụng trên trang Lovable';
64 }
65
66 document.body.appendChild(toggleButton);
67
68 // Nếu đang ở trang Lovable, cập nhật trạng thái nút
69 if (isLovablePage()) {
70 updateToggleButton();
71 }
72 }
73
74 // Cập nhật trạng thái của nút toggle
75 function updateToggleButton() {
76 const button = document.getElementById('automation-toggle');
77 if (button && isLovablePage()) {
78 if (STATE.isActive) {
79 button.textContent = 'Automation: ON';
80 button.style.backgroundColor = '#f44336';
81 } else {
82 button.textContent = 'Automation: OFF';
83 button.style.backgroundColor = '#4CAF50';
84 }
85 button.disabled = false;
86 button.style.opacity = '1';
87 button.title = '';
88 }
89 }
90
91 // Thiết lập observers để theo dõi thay đổi trên trang
92 function setupObservers() {
93 // Sử dụng MutationObserver để theo dõi thay đổi trên trang
94 STATE.observer = new MutationObserver(handleMutations);
95 STATE.observer.observe(document.body, {
96 childList: true,
97 subtree: true
98 });
99 }
100
101 // Tải trạng thái đã lưu từ storage
102 async function loadSavedState() {
103 try {
104 const savedState = await GM.getValue('lovableAssistantState', null);
105 if (savedState) {
106 STATE.tasks = savedState.tasks || [];
107 STATE.currentProject = savedState.currentProject || null;
108 STATE.isActive = savedState.isActive || false; // Khôi phục trạng thái automation
109 console.log('Loaded saved state:', savedState);
110
111 // Cập nhật giao diện sau khi tải trạng thái
112 if (isLovablePage()) {
113 updateToggleButton();
114 }
115 }
116 } catch (error) {
117 console.error('Error loading saved state:', error);
118 }
119 }
120
121 // Lưu trạng thái vào storage
122 async function saveState() {
123 try {
124 const stateToSave = {
125 tasks: STATE.tasks.map(task => ({
126 id: task.id,
127 title: task.title,
128 status: task.status
129 // Không lưu element vì nó không thể serialize
130 })),
131 currentProject: STATE.currentProject,
132 lastUpdated: new Date().toISOString(),
133 isActive: STATE.isActive // Lưu cả trạng thái automation
134 };
135
136 await GM.setValue('lovableAssistantState', stateToSave);
137 console.log('State saved successfully');
138 } catch (error) {
139 console.error('Error saving state:', error);
140 }
141 }
142
143 // Xử lý các thay đổi trên trang
144 function handleMutations(mutations) {
145 // Xử lý các mutation nếu cần
146 mutations.forEach(mutation => {
147 // Kiểm tra xem có phần tử mới được thêm vào không
148 if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
149 // Kiểm tra các nút mới được thêm vào
150 mutation.addedNodes.forEach(node => {
151 if (node.nodeType === Node.ELEMENT_NODE) {
152 // Kiểm tra xem có phải là phần tử chứa thông tin nhiệm vụ không
153 checkForTasks(node);
154 }
155 });
156 }
157 });
158 }
159
160 // Kiểm tra xem phần tử có chứa thông tin nhiệm vụ không
161 function checkForTasks(element) {
162 // Tìm các phần tử liên quan đến nhiệm vụ
163 const taskElements = element.querySelectorAll ?
164 element.querySelectorAll('[class*="task"], [class*="item"], [class*="card"]') : [];
165
166 if (taskElements.length > 0) {
167 console.log(`Found ${taskElements.length} potential task elements`);
168 // Xử lý các nhiệm vụ tìm được
169 processTasks(taskElements);
170 }
171 }
172
173 // Xử lý các nhiệm vụ tìm được
174 function processTasks(taskElements) {
175 let newStateAdded = false;
176
177 taskElements.forEach((taskElement, index) => {
178 // Trích xuất thông tin từ phần tử nhiệm vụ
179 const taskInfo = extractTaskInfo(taskElement);
180 if (taskInfo) {
181 // Kiểm tra xem nhiệm vụ đã tồn tại chưa
182 const existingTaskIndex = STATE.tasks.findIndex(t => t.id === taskInfo.id);
183
184 if (existingTaskIndex >= 0) {
185 // Cập nhật thông tin nhiệm vụ đã tồn tại
186 STATE.tasks[existingTaskIndex].title = taskInfo.title;
187 STATE.tasks[existingTaskIndex].status = taskInfo.status;
188 STATE.tasks[existingTaskIndex].element = taskElement;
189 } else {
190 // Thêm nhiệm vụ mới vào danh sách
191 STATE.tasks.push({
192 id: taskInfo.id || `task-${Date.now()}-${index}`,
193 title: taskInfo.title,
194 status: taskInfo.status || 'pending',
195 element: taskElement
196 });
197
198 console.log('Task added:', taskInfo.title);
199 newStateAdded = true;
200 }
201
202 // Nếu automation đang bật, thực hiện nhiệm vụ
203 if (STATE.isActive) {
204 const task = existingTaskIndex >= 0 ?
205 STATE.tasks[existingTaskIndex] :
206 STATE.tasks[STATE.tasks.length - 1];
207
208 if (task.status !== 'completed') {
209 executeTask(task);
210 }
211 }
212 }
213 });
214
215 // Lưu trạng thái nếu có nhiệm vụ mới
216 if (newStateAdded) {
217 saveState();
218 }
219 }
220
221 // Trích xuất thông tin từ phần tử nhiệm vụ
222 function extractTaskInfo(taskElement) {
223 // Thử lấy tiêu đề nhiệm vụ từ các thuộc tính phổ biến
224 const titleElement = taskElement.querySelector('[class*="title"], h1, h2, h3, h4, h5, h6, p') ||
225 taskElement.querySelector('span, div');
226
227 if (!titleElement) return null;
228
229 const title = titleElement.textContent.trim();
230 if (!title) return null;
231
232 // Thử lấy ID từ thuộc tính data
233 const id = taskElement.dataset.id || taskElement.id || null;
234
235 // Thử lấy trạng thái từ class hoặc nội dung
236 let status = 'pending';
237 const statusText = taskElement.textContent.toLowerCase();
238 if (statusText.includes('complete') || statusText.includes('done')) {
239 status = 'completed';
240 } else if (statusText.includes('progress') || statusText.includes('working')) {
241 status = 'in-progress';
242 }
243
244 return {
245 id: id,
246 title: title,
247 status: status
248 };
249 }
250
251 // Thực hiện một nhiệm vụ
252 function executeTask(task) {
253 console.log('Executing task:', task.title);
254
255 // Ở đây sẽ là logic để thực hiện nhiệm vụ
256 // Ví dụ: click vào nút hoàn thành, nhập dữ liệu, v.v.
257
258 // Giả lập thời gian thực hiện nhiệm vụ
259 setTimeout(() => {
260 task.status = 'completed';
261 console.log('Task completed:', task.title);
262
263 // Cập nhật giao diện người dùng
264 updateTaskUI(task);
265
266 // Lưu trạng thái sau khi hoàn thành nhiệm vụ
267 saveState();
268 }, 2000);
269 }
270
271 // Cập nhật giao diện người dùng khi nhiệm vụ thay đổi
272 function updateTaskUI(task) {
273 // Thêm hiệu ứng hoặc thay đổi màu sắc để biểu thị trạng thái
274 if (task.element) {
275 if (task.status === 'completed') {
276 task.element.style.borderLeft = '3px solid #4CAF50';
277 task.element.style.opacity = '0.8';
278 } else if (task.status === 'in-progress') {
279 task.element.style.borderLeft = '3px solid #2196F3';
280 task.element.style.opacity = '1';
281 } else {
282 task.element.style.borderLeft = '3px solid #FF9800';
283 task.element.style.opacity = '1';
284 }
285 }
286 }
287
288 // Bật/tắt tính năng automation
289 function toggleAutomation() {
290 // Chỉ cho phép toggle nếu đang ở trang Lovable
291 if (!isLovablePage()) return;
292
293 STATE.isActive = !STATE.isActive;
294 updateToggleButton();
295
296 if (STATE.isActive) {
297 console.log('Automation enabled');
298 } else {
299 console.log('Automation disabled');
300 }
301
302 // Lưu trạng thái
303 saveState();
304 }
305
306 // Chạy hàm khởi tạo khi trang tải xong
307 if (document.readyState === 'loading') {
308 document.addEventListener('DOMContentLoaded', init);
309 } else {
310 init();
311 }
312
313})();