Lovable Task Automation Assistant

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})();