IXL AI Answer Helper with Reasoning

Automatically provides AI-generated answers with detailed reasoning for IXL questions

Size

9.1 KB

Version

1.0.1

Created

Feb 26, 2026

Updated

about 2 months ago

1// ==UserScript==
2// @name		IXL AI Answer Helper with Reasoning
3// @description		Automatically provides AI-generated answers with detailed reasoning for IXL questions
4// @version		1.0.1
5// @match		https://*.ixl.com/*
6// @icon		https://www.ixl.com/ixl-favicon.png
7// ==/UserScript==
8(function() {
9    'use strict';
10
11    console.log('IXL AI Answer Helper initialized');
12
13    // Debounce function to prevent excessive calls
14    function debounce(func, wait) {
15        let timeout;
16        return function executedFunction(...args) {
17            const later = () => {
18                clearTimeout(timeout);
19                func(...args);
20            };
21            clearTimeout(timeout);
22            timeout = setTimeout(later, wait);
23        };
24    }
25
26    // Create UI container for displaying AI answer and reasoning
27    function createAnswerUI() {
28        // Check if UI already exists
29        if (document.getElementById('ixl-ai-helper-container')) {
30            return;
31        }
32
33        const container = document.createElement('div');
34        container.id = 'ixl-ai-helper-container';
35        container.style.cssText = `
36            position: fixed;
37            top: 20px;
38            right: 20px;
39            width: 350px;
40            max-height: 80vh;
41            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
42            border-radius: 12px;
43            padding: 20px;
44            box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3);
45            z-index: 999999;
46            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
47            color: white;
48            overflow-y: auto;
49            display: none;
50        `;
51
52        container.innerHTML = `
53            <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px;">
54                <h3 style="margin: 0; font-size: 18px; font-weight: 600;">AI Answer Helper</h3>
55                <button id="ixl-ai-close-btn" style="background: rgba(255,255,255,0.2); border: none; color: white; width: 28px; height: 28px; border-radius: 50%; cursor: pointer; font-size: 18px; line-height: 1;">Ɨ</button>
56            </div>
57            <div id="ixl-ai-status" style="margin-bottom: 15px; padding: 10px; background: rgba(255,255,255,0.15); border-radius: 8px; font-size: 13px; text-align: center;">
58                Waiting for question...
59            </div>
60            <div id="ixl-ai-answer" style="margin-bottom: 15px; display: none;">
61                <div style="font-weight: 600; margin-bottom: 8px; font-size: 14px; text-transform: uppercase; letter-spacing: 0.5px;">Answer:</div>
62                <div id="ixl-ai-answer-text" style="padding: 12px; background: rgba(255,255,255,0.95); color: #2d3748; border-radius: 8px; font-size: 16px; font-weight: 600;"></div>
63            </div>
64            <div id="ixl-ai-reasoning" style="display: none;">
65                <div style="font-weight: 600; margin-bottom: 8px; font-size: 14px; text-transform: uppercase; letter-spacing: 0.5px;">Reasoning:</div>
66                <div id="ixl-ai-reasoning-text" style="padding: 12px; background: rgba(255,255,255,0.15); border-radius: 8px; font-size: 14px; line-height: 1.6;"></div>
67            </div>
68        `;
69
70        document.body.appendChild(container);
71
72        // Close button functionality
73        document.getElementById('ixl-ai-close-btn').addEventListener('click', () => {
74            container.style.display = 'none';
75        });
76
77        console.log('AI Helper UI created');
78    }
79
80    // Update UI with status
81    function updateStatus(message, isLoading = false) {
82        const statusEl = document.getElementById('ixl-ai-status');
83        if (statusEl) {
84            statusEl.textContent = isLoading ? 'šŸ¤– ' + message : message;
85            statusEl.style.background = isLoading ? 'rgba(255,255,255,0.25)' : 'rgba(255,255,255,0.15)';
86        }
87    }
88
89    // Display answer and reasoning
90    function displayAnswer(answer, reasoning) {
91        const container = document.getElementById('ixl-ai-helper-container');
92        const answerDiv = document.getElementById('ixl-ai-answer');
93        const answerText = document.getElementById('ixl-ai-answer-text');
94        const reasoningDiv = document.getElementById('ixl-ai-reasoning');
95        const reasoningText = document.getElementById('ixl-ai-reasoning-text');
96
97        if (container && answerDiv && answerText && reasoningDiv && reasoningText) {
98            container.style.display = 'block';
99            answerDiv.style.display = 'block';
100            reasoningDiv.style.display = 'block';
101            answerText.textContent = answer;
102            reasoningText.textContent = reasoning;
103            updateStatus('āœ“ Answer ready!');
104        }
105    }
106
107    // Extract question text from the page
108    function extractQuestion() {
109        // IXL uses various selectors for questions, try multiple approaches
110        const selectors = [
111            '.qtext',
112            '[class*="question"]',
113            '[class*="Question"]',
114            '[data-test="question"]',
115            '.problem-text',
116            '[class*="problem"]'
117        ];
118
119        for (const selector of selectors) {
120            const element = document.querySelector(selector);
121            if (element && element.textContent.trim().length > 10) {
122                console.log('Found question with selector:', selector);
123                return element.textContent.trim();
124            }
125        }
126
127        // Fallback: look for the main content area
128        const mainContent = document.querySelector('main') || document.querySelector('[role="main"]');
129        if (mainContent) {
130            const text = mainContent.textContent.trim();
131            if (text.length > 20 && text.length < 5000) {
132                console.log('Extracted question from main content');
133                return text;
134            }
135        }
136
137        console.log('No question found on page');
138        return null;
139    }
140
141    // Get AI answer with reasoning
142    async function getAIAnswer(questionText) {
143        try {
144            console.log('Requesting AI answer for question:', questionText.substring(0, 100) + '...');
145            updateStatus('Analyzing question with AI...', true);
146
147            const prompt = `You are helping a student with an IXL question. Please provide:
1481. The correct answer (be concise and direct)
1492. A clear explanation of your reasoning
150
151Question: ${questionText}
152
153Provide your response in a structured format.`;
154
155            const response = await RM.aiCall(prompt, {
156                type: 'json_schema',
157                json_schema: {
158                    name: 'ixl_answer',
159                    schema: {
160                        type: 'object',
161                        properties: {
162                            answer: {
163                                type: 'string',
164                                description: 'The correct answer to the question'
165                            },
166                            reasoning: {
167                                type: 'string',
168                                description: 'Detailed explanation of why this is the correct answer'
169                            }
170                        },
171                        required: ['answer', 'reasoning']
172                    }
173                }
174            });
175
176            console.log('AI response received:', response);
177            return response;
178
179        } catch (error) {
180            console.error('Error getting AI answer:', error);
181            updateStatus('āŒ Error: ' + error.message);
182            return null;
183        }
184    }
185
186    // Process the current question
187    async function processQuestion() {
188        const questionText = extractQuestion();
189        
190        if (!questionText) {
191            console.log('No question detected yet');
192            updateStatus('Waiting for question...');
193            return;
194        }
195
196        // Check if we've already processed this question
197        const lastQuestion = await GM.getValue('last_question', '');
198        if (lastQuestion === questionText) {
199            console.log('Question already processed');
200            return;
201        }
202
203        // Store the current question
204        await GM.setValue('last_question', questionText);
205
206        console.log('Processing new question');
207        const result = await getAIAnswer(questionText);
208
209        if (result && result.answer && result.reasoning) {
210            displayAnswer(result.answer, result.reasoning);
211        }
212    }
213
214    // Debounced version of processQuestion
215    const debouncedProcessQuestion = debounce(processQuestion, 1000);
216
217    // Initialize the extension
218    function init() {
219        console.log('Initializing IXL AI Answer Helper');
220        
221        // Create the UI
222        createAnswerUI();
223
224        // Process question immediately if present
225        setTimeout(processQuestion, 2000);
226
227        // Watch for DOM changes (new questions loading)
228        const observer = new MutationObserver(debouncedProcessQuestion);
229        
230        observer.observe(document.body, {
231            childList: true,
232            subtree: true
233        });
234
235        console.log('Observer started, watching for questions');
236    }
237
238    // Start when page is ready
239    if (document.readyState === 'loading') {
240        document.addEventListener('DOMContentLoaded', init);
241    } else {
242        init();
243    }
244
245})();