// ==UserScript== // @name LeetCode Token Golf // @namespace http://tampermonkey.net/ // @version 0.1.1 // @description Track token efficiency on LeetCode - your API key never leaves your browser // @author TokenGolf // @match https://leetcode.com/problems/* // @match https://leetcode.cn/problems/* // @icon data:image/svg+xml, // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // @grant GM_xmlhttpRequest // @connect api.openai.com // @connect api.anthropic.com // @run-at document-idle // ==/UserScript== (function() { 'use strict'; // ════════════════════════════════════════════════════════════ // 🧩 BUBBLE 1: STORAGE LAYER (Pure Core - No Side Effects) // ════════════════════════════════════════════════════════════ const StorageBubble = { // Pure functions for data transformation _createRunKey(problemId) { return `token_golf_runs_${problemId}`; }, _serializeRun(run) { return JSON.stringify({ problemId: run.problemId, timestamp: run.timestamp, prompt: run.prompt, tokens: run.tokens, model: run.model, passed: run.passed, response: run.response }); }, _deserializeRuns(json) { try { return JSON.parse(json || '[]'); } catch { return []; } }, // Public interface (message-passing style) saveRun(run) { const key = this._createRunKey(run.problemId); const existing = this._deserializeRuns(GM_getValue(key)); existing.push(run); GM_setValue(key, JSON.stringify(existing)); return { success: true }; }, getRuns(problemId) { const key = this._createRunKey(problemId); return this._deserializeRuns(GM_getValue(key)); }, getAPIKey(provider = 'openai') { return GM_getValue(`token_golf_api_key_${provider}`, null); }, setAPIKey(provider, key) { GM_setValue(`token_golf_api_key_${provider}`, key); return { success: true }; }, clearRuns(problemId) { const key = this._createRunKey(problemId); GM_deleteValue(key); return { success: true }; } }; // ════════════════════════════════════════════════════════════ // 🧩 BUBBLE 2: LLM PROXY (I/O Shell - Side Effects Isolated) // ════════════════════════════════════════════════════════════ const LLMProxyBubble = { // Token estimation (pure function) estimateTokens(text) { // Rough estimation: ~4 chars per token return Math.ceil(text.length / 4); }, // OpenAI API call async _callOpenAI(prompt, model, apiKey) { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'POST', url: 'https://api.openai.com/v1/chat/completions', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${apiKey}` }, data: JSON.stringify({ model: model || 'gpt-4o-mini', messages: [ { role: 'user', content: prompt } ], temperature: 0.7 }), onload: (response) => { try { const data = JSON.parse(response.responseText); if (data.error) { reject(new Error(data.error.message)); return; } resolve({ text: data.choices[0].message.content, tokens: data.usage.total_tokens, model: data.model }); } catch (e) { reject(e); } }, onerror: (err) => reject(err) }); }); }, // Anthropic API call async _callAnthropic(prompt, model, apiKey) { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'POST', url: 'https://api.anthropic.com/v1/messages', headers: { 'Content-Type': 'application/json', 'x-api-key': apiKey, 'anthropic-version': '2023-06-01' }, data: JSON.stringify({ model: model || 'claude-3-5-haiku-20241022', max_tokens: 4096, messages: [ { role: 'user', content: prompt } ] }), onload: (response) => { try { const data = JSON.parse(response.responseText); if (data.error) { reject(new Error(data.error.message)); return; } const tokens = data.usage.input_tokens + data.usage.output_tokens; resolve({ text: data.content[0].text, tokens: tokens, model: data.model }); } catch (e) { reject(e); } }, onerror: (err) => reject(err) }); }); }, // Public interface async callLLM(prompt, provider, model, apiKey) { if (!apiKey) { throw new Error('API key not set'); } if (provider === 'anthropic') { return await this._callAnthropic(prompt, model, apiKey); } else { return await this._callOpenAI(prompt, model, apiKey); } } }; // ════════════════════════════════════════════════════════════ // 🧩 BUBBLE 3: UI CORE (Transform Layer - Pure Rendering) // ════════════════════════════════════════════════════════════ const UIBubble = { state: { currentRun: null, history: [], isRunning: false, provider: 'openai', model: 'gpt-4o-mini' }, // Pure template functions _createPanelHTML() { return `
⛳ Token Golf
Recent Runs
`; }, _createHistoryItemHTML(run, index) { const passedIcon = run.passed === null ? '⏳' : run.passed ? '✅' : '❌'; const date = new Date(run.timestamp).toLocaleTimeString(); return `
${passedIcon} ${run.tokens} tokens ${date}
${run.model}
`; }, // Public interface renderPanel() { const panel = document.createElement('div'); panel.innerHTML = this._createPanelHTML(); return panel.firstElementChild; }, updateHistory(runs) { const listEl = document.getElementById('tg-history-list'); if (!listEl) return; if (runs.length === 0) { listEl.innerHTML = '
No runs yet
'; return; } listEl.innerHTML = runs.slice(-5).reverse() .map((run, i) => this._createHistoryItemHTML(run, i)) .join(''); }, showResult(response) { const resultEl = document.getElementById('tg-result'); const errorEl = document.getElementById('tg-error'); if (!resultEl || !errorEl) return; errorEl.style.display = 'none'; resultEl.style.display = 'block'; document.getElementById('tg-tokens').textContent = response.tokens; document.getElementById('tg-model-used').textContent = response.model; this.state.currentRun = response; }, showError(message) { const errorEl = document.getElementById('tg-error'); const resultEl = document.getElementById('tg-result'); if (!errorEl || !resultEl) return; resultEl.style.display = 'none'; errorEl.style.display = 'block'; errorEl.textContent = `❌ ${message}`; }, setLoading(isLoading) { const btn = document.getElementById('tg-run-btn'); if (!btn) return; this.state.isRunning = isLoading; btn.disabled = isLoading; btn.style.opacity = isLoading ? '0.6' : '1'; btn.textContent = isLoading ? '⏳ Running...' : '🚀 Run with AI'; } }; // ════════════════════════════════════════════════════════════ // 🧩 BUBBLE 4: LEETCODE DOM (I/O Shell - Page Integration) // ════════════════════════════════════════════════════════════ const LeetCodeDOMBubble = { // Extract problem ID from URL getProblemId() { const match = window.location.pathname.match(/\/problems\/([^\/]+)/); return match ? match[1] : 'unknown'; }, // Get editor content getEditorContent() { // Try Monaco editor first (new LeetCode UI) const monacoEditor = document.querySelector('.monaco-editor'); if (monacoEditor) { try { const model = window.monaco?.editor?.getModels()?.[0]; if (model) return model.getValue(); } catch (e) { console.error('Monaco editor access failed:', e); } } // Fallback: try CodeMirror const codeMirror = document.querySelector('.CodeMirror'); if (codeMirror && codeMirror.CodeMirror) { return codeMirror.CodeMirror.getValue(); } // Last resort: textarea const textarea = document.querySelector('textarea'); return textarea ? textarea.value : ''; }, // Set editor content setEditorContent(content) { // Try Monaco editor const monacoEditor = document.querySelector('.monaco-editor'); if (monacoEditor) { try { const model = window.monaco?.editor?.getModels()?.[0]; if (model) { model.setValue(content); return true; } } catch (e) { console.error('Monaco editor write failed:', e); } } // Fallback: CodeMirror const codeMirror = document.querySelector('.CodeMirror'); if (codeMirror && codeMirror.CodeMirror) { codeMirror.CodeMirror.setValue(content); return true; } // Last resort: textarea const textarea = document.querySelector('textarea'); if (textarea) { textarea.value = content; return true; } return false; }, // Inject UI into page injectUI(panelElement) { // Remove existing panel if present const existing = document.getElementById('token-golf-panel'); if (existing) existing.remove(); document.body.appendChild(panelElement); }, // Detect submission result (watches DOM for changes) observeSubmissions(callback) { // Watch for submission result modal/panel const observer = new MutationObserver((mutations) => { for (const mutation of mutations) { for (const node of mutation.addedNodes) { if (node.nodeType === 1) { // Look for accepted/wrong answer indicators const text = node.textContent || ''; if (text.includes('Accepted')) { callback({ passed: true, timestamp: Date.now() }); } else if (text.includes('Wrong Answer') || text.includes('Runtime Error')) { callback({ passed: false, timestamp: Date.now() }); } } } } }); observer.observe(document.body, { childList: true, subtree: true }); return observer; } }; // ════════════════════════════════════════════════════════════ // 🔄 MESSAGE PASSING ORCHESTRATOR (Integration Layer) // ════════════════════════════════════════════════════════════ const App = { async init() { console.log('🏌️ Token Golf initializing...'); // Render UI const panel = UIBubble.renderPanel(); LeetCodeDOMBubble.injectUI(panel); // Load state const problemId = LeetCodeDOMBubble.getProblemId(); const history = StorageBubble.getRuns(problemId); UIBubble.updateHistory(history); // Bind events this._bindEvents(); // Load saved settings this._loadSettings(); // Observe submissions LeetCodeDOMBubble.observeSubmissions((result) => { this._handleSubmissionResult(result); }); console.log('✅ Token Golf ready'); }, _loadSettings() { const provider = GM_getValue('token_golf_provider', 'openai'); const model = GM_getValue('token_golf_model', 'gpt-4o-mini'); const apiKey = StorageBubble.getAPIKey(provider); document.getElementById('tg-provider').value = provider; document.getElementById('tg-model').value = model; if (apiKey) { document.getElementById('tg-api-key').value = apiKey; } UIBubble.state.provider = provider; UIBubble.state.model = model; }, _bindEvents() { // Settings toggle document.getElementById('tg-settings-btn').onclick = () => { const settings = document.getElementById('tg-settings'); settings.style.display = settings.style.display === 'none' ? 'block' : 'none'; }; // CRITICAL: All input handlers MUST update UIBubble.state // State is the single source of truth, not the DOM // Provider change document.getElementById('tg-provider').onchange = (e) => { const provider = e.target.value; UIBubble.state.provider = provider; GM_setValue('token_golf_provider', provider); // Load saved API key for this provider const apiKey = StorageBubble.getAPIKey(provider); if (apiKey) { document.getElementById('tg-api-key').value = apiKey; } else { document.getElementById('tg-api-key').value = ''; } }; // Model change document.getElementById('tg-model').oninput = (e) => { UIBubble.state.model = e.target.value; GM_setValue('token_golf_model', e.target.value); }; // API key change document.getElementById('tg-api-key').onchange = (e) => { const provider = UIBubble.state.provider; StorageBubble.setAPIKey(provider, e.target.value); }; // Run button document.getElementById('tg-run-btn').onclick = () => { this._handleRunClick(); }; // Copy button document.getElementById('tg-copy-btn').onclick = () => { this._handleCopyClick(); }; }, async _handleRunClick() { const prompt = LeetCodeDOMBubble.getEditorContent(); if (!prompt || prompt.trim().length === 0) { UIBubble.showError('Editor is empty. Write your prompt first.'); return; } // Read from state (single source of truth) const provider = UIBubble.state.provider; const model = UIBubble.state.model || 'gpt-4o-mini'; const apiKey = StorageBubble.getAPIKey(provider); if (!apiKey) { UIBubble.showError('API key not set. Click ⚙️ to configure.'); return; } UIBubble.setLoading(true); try { // Call LLM const response = await LLMProxyBubble.callLLM(prompt, provider, model, apiKey); // Show result UIBubble.showResult(response); // Save run (without pass/fail yet) const problemId = LeetCodeDOMBubble.getProblemId(); const run = { problemId, timestamp: Date.now(), prompt, tokens: response.tokens, model: response.model, passed: null, response: response.text }; StorageBubble.saveRun(run); // Update history const history = StorageBubble.getRuns(problemId); UIBubble.updateHistory(history); // Set editor content to response LeetCodeDOMBubble.setEditorContent(response.text); } catch (error) { UIBubble.showError(error.message || 'Request failed'); console.error('LLM call failed:', error); } finally { UIBubble.setLoading(false); } }, _handleCopyClick() { const response = UIBubble.state.currentRun; if (!response) return; navigator.clipboard.writeText(response.text).then(() => { const btn = document.getElementById('tg-copy-btn'); const originalText = btn.textContent; btn.textContent = '✓ Copied!'; setTimeout(() => { btn.textContent = originalText; }, 2000); }); }, _handleSubmissionResult(result) { // Update the last run with pass/fail status const problemId = LeetCodeDOMBubble.getProblemId(); const runs = StorageBubble.getRuns(problemId); if (runs.length === 0) return; const lastRun = runs[runs.length - 1]; if (lastRun.passed === null) { lastRun.passed = result.passed; // Re-save entire history GM_setValue(`token_golf_runs_${problemId}`, JSON.stringify(runs)); // Update UI UIBubble.updateHistory(runs); } } }; // ════════════════════════════════════════════════════════════ // 🚀 BOOTSTRAP // ════════════════════════════════════════════════════════════ // Wait for page to be ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => App.init()); } else { setTimeout(() => App.init(), 1000); } // Handle SPA navigation (LeetCode doesn't full reload) let lastUrl = location.href; new MutationObserver(() => { const url = location.href; if (url !== lastUrl) { lastUrl = url; if (url.includes('/problems/')) { setTimeout(() => App.init(), 500); } } }).observe(document.body, { childList: true, subtree: true }); })();