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