// ==UserScript== // @name Claude Token Saver v35 // @namespace http://tampermonkey.net/ // @version 35 // @description Prevents token waste by enforcing file creation instead of chat pasting. Monitors output length and detects file creation. FIXED: Copy command, monitoring, visual feedback. // @author You // @match https://claude.ai/* // @grant none // @downloadURL none // ==/UserScript== (function() { 'use strict'; console.log('๐ŸŽฏ Token Saver v35 - Script loaded!'); // ==================== CONFIGURATION ==================== const CONFIG = { warningThreshold: 500, dangerThreshold: 1000, checkInterval: 500, fileCheckInterval: 2000, statusUpdateDebounce: 2000, pastePatterns: [ '```markdown', '```md', '# ', '## ', '### ', '---\n', '\n\n\n' ], fileKeywords: [ 'download', 'computer://', '/mnt/user-data/outputs/', 'view your', 'file created', 'saved to' ] }; // ==================== STATE MANAGEMENT ==================== const state = { monitoring: { isActive: false, responseLength: 0, hasFileMention: false, fileDetectedPermanently: false }, ui: { isMaximized: false, statusIndicator: null, lastStatusType: null, lastUpdateTime: 0 }, files: { detected: [], observerActive: false }, drag: { panel: { active: false, x: 0, y: 0, startX: 0, startY: 0 }, status: { active: false, x: 0, y: 0, startX: 0, startY: 0 } } }; // ==================== UTILITY FUNCTIONS ==================== const utils = { debounce(func, wait) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; }, copyToClipboard(text, successMessage) { console.log('๐Ÿ“‹ Attempting to copy:', text.substring(0, 100) + '...'); // Modern Clipboard API if (navigator.clipboard && navigator.clipboard.writeText) { navigator.clipboard.writeText(text) .then(() => { console.log('โœ… Copy successful!'); statusManager.update('safe', successMessage); }) .catch((err) => { console.error('โŒ Clipboard API failed:', err); this.fallbackCopy(text, successMessage); }); } else { this.fallbackCopy(text, successMessage); } }, fallbackCopy(text, successMessage) { // Fallback method using textarea const textarea = document.createElement('textarea'); textarea.value = text; textarea.style.position = 'fixed'; textarea.style.opacity = '0'; document.body.appendChild(textarea); textarea.select(); try { const success = document.execCommand('copy'); if (success) { console.log('โœ… Fallback copy successful!'); statusManager.update('safe', successMessage); } else { console.error('โŒ Fallback copy failed'); this.showManualCopy(text); } } catch (err) { console.error('โŒ Copy error:', err); this.showManualCopy(text); } finally { document.body.removeChild(textarea); } }, showManualCopy(text) { const modal = document.createElement('div'); modal.style.cssText = ` position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: white; padding: 20px; border-radius: 10px; box-shadow: 0 4px 30px rgba(0,0,0,0.3); z-index: 10001; max-width: 500px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; `; modal.innerHTML = `
๐Ÿ“‹ Copy This Command
`; document.body.appendChild(modal); modal.querySelector('textarea').select(); modal.querySelector('#close-modal').addEventListener('click', () => { document.body.removeChild(modal); }); } }; // ==================== STATUS INDICATOR MANAGER ==================== const statusManager = { create() { const indicator = document.createElement('div'); indicator.id = 'workflow-status'; // v35: Changed default position from top 20px to top 100px to avoid overlap with Usage Tracker indicator.style.cssText = ` position: fixed; top: 100px; right: 20px; padding: 12px 20px 12px 12px; border-radius: 8px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; font-size: 13px; font-weight: 600; z-index: 10000; box-shadow: 0 4px 20px rgba(0,0,0,0.15); display: none; cursor: grab; user-select: none; will-change: transform; transition: background-color 0.3s ease; `; indicator.addEventListener('mousedown', dragManager.statusStart, { passive: false }); document.body.appendChild(indicator); state.ui.statusIndicator = indicator; return indicator; }, update(status, message) { console.log(`๐Ÿ“Š Status update: ${status} - ${message}`); if (status === 'safe' && message.includes('File creation detected')) { state.monitoring.fileDetectedPermanently = true; } if (state.monitoring.fileDetectedPermanently && status !== 'safe') { return; } const now = Date.now(); const timeSinceLastUpdate = now - state.ui.lastUpdateTime; if (status === state.ui.lastStatusType && timeSinceLastUpdate < CONFIG.statusUpdateDebounce) { return; } state.ui.lastStatusType = status; state.ui.lastUpdateTime = now; if (!state.ui.statusIndicator) { this.create(); } const indicator = state.ui.statusIndicator; indicator.style.display = 'flex'; indicator.style.alignItems = 'center'; indicator.style.gap = '8px'; const statusConfig = { safe: { bg: '#10b981', text: '#ffffff', icon: 'โœ“' }, warning: { bg: '#f59e0b', text: '#ffffff', icon: 'โš ' }, danger: { bg: '#ef4444', text: '#ffffff', icon: 'โœ•' }, info: { bg: '#06b6d4', text: '#ffffff', icon: 'โ„น' } }; const config = statusConfig[status] || statusConfig.info; indicator.style.backgroundColor = config.bg; indicator.style.color = config.text; indicator.innerHTML = `${config.icon} ${message}`; indicator.appendChild(this.createCloseButton()); }, createCloseButton() { const closeBtn = document.createElement('button'); closeBtn.textContent = 'โœ•'; closeBtn.style.cssText = ` background: rgba(0,0,0,0.2); border: none; color: inherit; cursor: pointer; padding: 4px 8px; border-radius: 4px; font-weight: bold; font-size: 11px; transition: background 0.2s; `; closeBtn.addEventListener('mouseenter', () => closeBtn.style.background = 'rgba(0,0,0,0.3)'); closeBtn.addEventListener('mouseleave', () => closeBtn.style.background = 'rgba(0,0,0,0.2)'); closeBtn.addEventListener('click', (e) => { e.stopPropagation(); this.close(); }); return closeBtn; }, close() { if (state.ui.statusIndicator) { state.ui.statusIndicator.style.display = 'none'; } } }; // ==================== DRAG MANAGER (OPTIMIZED) ==================== const dragManager = { panelStart(e) { // Skip if clicking on buttons if (e.target.tagName === 'BUTTON' || e.target.closest('button')) { return; } // Skip if clicking on clickable file items if (e.target.closest('#file-list > div')) { return; } e.preventDefault(); const panel = document.getElementById('workflow-panel'); const rect = panel.getBoundingClientRect(); state.drag.panel.active = true; state.drag.panel.startX = e.clientX; state.drag.panel.startY = e.clientY; state.drag.panel.x = rect.left; state.drag.panel.y = rect.top; if (panel) { panel.style.cursor = 'grabbing'; panel.style.transition = 'none'; } document.body.style.userSelect = 'none'; }, panelMove(e) { if (!state.drag.panel.active) return; e.preventDefault(); const deltaX = e.clientX - state.drag.panel.startX; const deltaY = e.clientY - state.drag.panel.startY; const newX = state.drag.panel.x + deltaX; const newY = state.drag.panel.y + deltaY; const panel = document.getElementById('workflow-panel'); if (panel) { panel.style.left = newX + 'px'; panel.style.top = newY + 'px'; panel.style.right = 'auto'; panel.style.bottom = 'auto'; } }, panelEnd() { if (!state.drag.panel.active) return; state.drag.panel.active = false; const panel = document.getElementById('workflow-panel'); if (panel) { panel.style.cursor = 'grab'; } document.body.style.userSelect = ''; }, statusStart(e) { e.preventDefault(); e.stopPropagation(); const indicator = state.ui.statusIndicator; const rect = indicator.getBoundingClientRect(); state.drag.status.active = true; state.drag.status.startX = e.clientX; state.drag.status.startY = e.clientY; state.drag.status.x = rect.left; state.drag.status.y = rect.top; if (indicator) { indicator.style.cursor = 'grabbing'; indicator.style.transition = 'none'; } }, statusMove(e) { if (!state.drag.status.active || !state.ui.statusIndicator) return; e.preventDefault(); const deltaX = e.clientX - state.drag.status.startX; const deltaY = e.clientY - state.drag.status.startY; const newX = state.drag.status.x + deltaX; const newY = state.drag.status.y + deltaY; state.ui.statusIndicator.style.left = newX + 'px'; state.ui.statusIndicator.style.top = newY + 'px'; state.ui.statusIndicator.style.right = 'auto'; }, statusEnd() { if (!state.drag.status.active) return; state.drag.status.active = false; if (state.ui.statusIndicator) { state.ui.statusIndicator.style.cursor = 'grab'; } }, init() { document.addEventListener('mousemove', (e) => { this.panelMove(e); this.statusMove(e); }, { passive: false }); document.addEventListener('mouseup', () => { this.panelEnd(); this.statusEnd(); }); } }; // ==================== MONITORING MANAGER ==================== const monitoringManager = { detectPastePattern(text) { return CONFIG.pastePatterns.some(pattern => text.includes(pattern)); }, detectFileMention(text) { return CONFIG.fileKeywords.some(keyword => text.toLowerCase().includes(keyword.toLowerCase()) ); }, checkResponse() { const responseElements = document.querySelectorAll('[data-test-render-count]'); if (responseElements.length === 0) { state.monitoring.isActive = false; return; } const latestResponse = responseElements[responseElements.length - 1]; const responseText = latestResponse.textContent || ''; state.monitoring.responseLength = responseText.length; if (!state.monitoring.hasFileMention) { state.monitoring.hasFileMention = this.detectFileMention(responseText); } if (state.monitoring.hasFileMention) { statusManager.update('safe', 'โœ… File creation detected'); } else if (state.monitoring.responseLength > CONFIG.dangerThreshold && this.detectPastePattern(responseText)) { statusManager.update('danger', 'โŒ Paste detected - may timeout'); } else if (state.monitoring.responseLength > CONFIG.warningThreshold) { statusManager.update('warning', `โš ๏ธ ${state.monitoring.responseLength} chars, no file yet`); } if (state.monitoring.isActive) { setTimeout(() => this.checkResponse(), CONFIG.checkInterval); } }, start() { console.log('๐Ÿ” Starting response monitoring...'); state.monitoring.isActive = true; state.monitoring.responseLength = 0; state.monitoring.hasFileMention = false; state.monitoring.fileDetectedPermanently = false; this.checkResponse(); }, observeMessages() { const observer = new MutationObserver((mutations) => { const hasNewResponse = mutations.some(mutation => Array.from(mutation.addedNodes).some(node => node.nodeType === 1 && ( node.querySelector('[data-test-render-count]') || node.hasAttribute('data-test-render-count') ) ) ); if (hasNewResponse && !state.monitoring.isActive) { console.log('๐Ÿ†• New response detected, starting monitoring'); this.start(); } }); observer.observe(document.body, { childList: true, subtree: true }); console.log('๐Ÿ‘€ Message observer active'); } }; // ==================== FILE MANAGER ==================== const fileManager = { detect() { const fileElements = document.querySelectorAll( '[class*="attachment"], [class*="file"], [data-testid*="file"], a[href*="uploads"]' ); const newFiles = []; fileElements.forEach(el => { const fileName = el.textContent || el.getAttribute('title') || el.getAttribute('aria-label') || el.getAttribute('href'); if (fileName && (fileName.includes('.') || fileName.includes('uploads/'))) { const cleanName = fileName.split('/').pop().trim(); if (cleanName && cleanName.length > 0) { newFiles.push(cleanName); } } }); if (newFiles.length > state.files.detected.length) { state.files.detected = [...new Set(newFiles)]; console.log('๐Ÿ“ Files detected:', state.files.detected); this.updateList(); statusManager.update('info', `๐Ÿ“‚ ${newFiles.length} file(s) detected`); } }, updateList() { const listContainer = document.getElementById('file-list'); if (!listContainer) return; if (state.files.detected.length === 0) { listContainer.innerHTML = `
No files detected
Upload via Claude
`; return; } listContainer.innerHTML = ''; state.files.detected.forEach((fileName) => { const fileDiv = this.createFileItem(fileName); listContainer.appendChild(fileDiv); }); }, createFileItem(fileName) { const fileDiv = document.createElement('div'); fileDiv.style.cssText = ` background: #ffffff; padding: 8px; border-radius: 6px; margin-bottom: 4px; font-size: 11px; cursor: pointer; border: 1px solid #e2e8f0; transition: all 0.2s; `; fileDiv.addEventListener('mouseenter', () => { fileDiv.style.background = '#f1f5f9'; fileDiv.style.borderColor = '#06b6d4'; }); fileDiv.addEventListener('mouseleave', () => { fileDiv.style.background = '#ffffff'; fileDiv.style.borderColor = '#e2e8f0'; }); fileDiv.addEventListener('click', (e) => { e.stopPropagation(); console.log('๐Ÿ–ฑ๏ธ File clicked:', fileName); this.copyViewCommand(fileName); }); fileDiv.innerHTML = `
๐Ÿ“„ ${fileName}
Click to copy command
`; return fileDiv; }, copyViewCommand(fileName) { const command = `Please use the view tool to read this file: /mnt/user-data/uploads/${fileName} Then process it according to my instructions and create a downloadable output file in /mnt/user-data/outputs/ - do NOT paste content in chat.`; console.log('๐Ÿ“‹ Copying command for:', fileName); utils.copyToClipboard(command, `โœ… Command copied for ${fileName}`); }, scanUploads() { const command = `Please use the view tool to scan /mnt/user-data/uploads and tell me what files are there. List their exact names.`; console.log('๐Ÿ” Copying scan command'); utils.copyToClipboard(command, 'โœ… Scan command copied - paste in chat'); }, clear() { state.files.detected = []; this.updateList(); statusManager.update('info', '๐Ÿ—‘๏ธ File list cleared'); }, startPeriodicCheck() { setInterval(() => this.detect(), CONFIG.fileCheckInterval); console.log('โฐ Periodic file check started'); } }; // ==================== UI MANAGER ==================== const uiManager = { toggleMaximize() { const panel = document.getElementById('workflow-panel'); const maximizeBtn = document.getElementById('maximize-btn'); const testBtn = document.getElementById('test-status-btn'); const dragHandle = panel.querySelector('.drag-handle'); if (!panel || !maximizeBtn) { console.error('โŒ Panel or button not found!'); return; } state.ui.isMaximized = !state.ui.isMaximized; console.log('๐Ÿ”„ Toggle maximize:', state.ui.isMaximized); const sections = panel.querySelectorAll('.panel-section'); const bottomDiv = document.getElementById('bottom-buttons'); sections.forEach((section) => { section.style.display = state.ui.isMaximized ? 'flex' : 'none'; }); // Enable smooth transitions for size changes panel.style.transition = 'all 0.3s ease'; setTimeout(() => { panel.style.transition = 'none'; }, 300); if (state.ui.isMaximized) { // Maximized state maximizeBtn.textContent = 'โž–'; testBtn.style.display = 'block'; panel.style.width = 'auto'; panel.style.minWidth = '600px'; panel.style.maxWidth = '650px'; panel.style.cursor = 'grab'; panel.style.padding = '12px'; dragHandle.style.margin = '-6px -6px 10px -6px'; bottomDiv.style.flexDirection = 'row'; bottomDiv.style.gap = '6px'; bottomDiv.style.marginTop = '10px'; } else { // Minimized state maximizeBtn.textContent = 'โž•'; testBtn.style.display = 'none'; panel.style.width = '60px'; panel.style.minWidth = '60px'; panel.style.maxWidth = '60px'; panel.style.cursor = 'grab'; panel.style.padding = '10px'; dragHandle.style.margin = '-6px -6px 8px -6px'; bottomDiv.style.flexDirection = 'column'; bottomDiv.style.gap = '0'; bottomDiv.style.marginTop = '8px'; } }, createButton(config) { const btn = document.createElement('button'); btn.id = config.id; btn.textContent = config.text; btn.style.cssText = config.style; if (config.hover) { btn.addEventListener('mouseenter', () => Object.assign(btn.style, config.hover.in)); btn.addEventListener('mouseleave', () => Object.assign(btn.style, config.hover.out)); } if (config.onClick) { btn.addEventListener('click', (e) => { e.stopPropagation(); config.onClick(e); }); } return btn; }, injectStyles() { const style = document.createElement('style'); style.textContent = ` #workflow-panel { will-change: auto; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-rendering: optimizeLegibility; } #file-list::-webkit-scrollbar { width: 4px; } #file-list::-webkit-scrollbar-track { background: #f1f5f9; } #file-list::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 2px; } #file-list::-webkit-scrollbar-thumb:hover { background: #94a3b8; } `; document.head.appendChild(style); }, createPanel() { const existingPanel = document.getElementById('workflow-panel'); if (existingPanel) { existingPanel.remove(); } this.injectStyles(); const panel = document.createElement('div'); panel.id = 'workflow-panel'; // v35: Changed default position from bottom-right to top-right // Start with minimized styles at top right panel.style.cssText = ` position: fixed; top: 20px; right: 100px; background: linear-gradient(135deg, #f8fafc 0%, #e0f2fe 100%); padding: 10px; border-radius: 10px; box-shadow: 0 4px 20px rgba(0,0,0,0.15); z-index: 9999; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; color: #1e293b; min-width: 60px; max-width: 60px; width: 60px; cursor: grab; border: 2px solid #cbd5e1; `; // Drag handle const dragHandle = document.createElement('div'); dragHandle.className = 'drag-handle'; dragHandle.style.cssText = ` font-size: 24px; cursor: grab; padding: 6px; margin: -6px -6px 8px -6px; border-radius: 8px; text-align: center; background: linear-gradient(135deg, #06b6d4 0%, #0891b2 100%); transition: all 0.2s; color: white; font-weight: 700; display: flex; align-items: center; justify-content: center; `; dragHandle.innerHTML = '๐Ÿ’พ'; dragHandle.addEventListener('mouseenter', () => dragHandle.style.transform = 'scale(1.05)'); dragHandle.addEventListener('mouseleave', () => dragHandle.style.transform = 'scale(1)'); // Main content section const mainSection = document.createElement('div'); mainSection.className = 'panel-section'; mainSection.style.cssText = ` display: none; gap: 10px; margin-bottom: 10px; `; const col1 = this.createFeaturesColumn(); const col2 = this.createButtonsColumn(); const col3 = this.createFileListColumn(); const col4 = this.createHowItWorksColumn(); mainSection.appendChild(col1); mainSection.appendChild(col2); mainSection.appendChild(col3); mainSection.appendChild(col4); const bottomDiv = this.createBottomButtons(); bottomDiv.style.flexDirection = 'column'; bottomDiv.style.gap = '0'; bottomDiv.style.marginTop = '8px'; panel.appendChild(dragHandle); panel.appendChild(mainSection); panel.appendChild(bottomDiv); document.body.appendChild(panel); panel.addEventListener('mousedown', dragManager.panelStart, { passive: false }); return panel; }, createFeaturesColumn() { const col = document.createElement('div'); col.className = 'panel-section'; col.style.cssText = ` flex: 1; background: #ffffff; padding: 10px; border-radius: 6px; border: 1px solid #e2e8f0; display: flex; flex-direction: column; `; col.innerHTML = `
๐Ÿ’พ TOKEN SAVING
โœ“ Prevents pasting
โœ“ Enforces files
โœ“ Length monitor
โœ“ Timeout prevention
`; return col; }, createButtonsColumn() { const col = document.createElement('div'); col.className = 'panel-section'; col.style.cssText = ` flex: 1; display: flex; flex-direction: column; gap: 5px; `; const scanBtn = this.createButton({ id: 'scan-uploads-btn', text: '๐Ÿ” Scan', style: ` width: 100%; padding: 8px; background: linear-gradient(135deg, #06b6d4 0%, #0891b2 100%); color: white; border: none; border-radius: 6px; font-weight: 600; cursor: pointer; font-size: 11px; transition: all 0.2s; box-shadow: 0 2px 6px rgba(6,182,212,0.2); `, hover: { in: { transform: 'translateY(-1px)', boxShadow: '0 4px 10px rgba(6,182,212,0.3)' }, out: { transform: 'translateY(0)', boxShadow: '0 2px 6px rgba(6,182,212,0.2)' } }, onClick: () => { console.log('๐Ÿ” Scan button clicked'); fileManager.scanUploads(); } }); const refreshBtn = this.createButton({ id: 'refresh-files-btn', text: '๐Ÿ”„ Refresh', style: ` width: 100%; padding: 8px; background: #cbd5e1; color: #1e293b; border: none; border-radius: 6px; font-weight: 600; cursor: pointer; font-size: 11px; transition: all 0.2s; `, hover: { in: { background: '#94a3b8', color: '#ffffff' }, out: { background: '#cbd5e1', color: '#1e293b' } }, onClick: () => { console.log('๐Ÿ”„ Refresh button clicked'); fileManager.detect(); statusManager.update('info', '๐Ÿ”„ Refreshing file list'); } }); const clearBtn = this.createButton({ id: 'clear-files-btn', text: '๐Ÿ—‘๏ธ Clear', style: ` width: 100%; padding: 8px; background: #f1f5f9; color: #64748b; border: 1px solid #e2e8f0; border-radius: 6px; cursor: pointer; font-size: 11px; transition: all 0.2s; `, hover: { in: { background: '#e2e8f0', color: '#334155' }, out: { background: '#f1f5f9', color: '#64748b' } }, onClick: () => { console.log('๐Ÿ—‘๏ธ Clear button clicked'); fileManager.clear(); } }); col.appendChild(scanBtn); col.appendChild(refreshBtn); col.appendChild(clearBtn); return col; }, createFileListColumn() { const col = document.createElement('div'); col.className = 'panel-section'; col.style.cssText = ` flex: 2; background: #ffffff; padding: 8px; border-radius: 6px; max-height: 120px; overflow-y: auto; display: flex; flex-direction: column; border: 1px solid #e2e8f0; `; col.innerHTML = `
๐Ÿ“‚ DETECTED FILES
No files detected
Upload via Claude
`; return col; }, createHowItWorksColumn() { const col = document.createElement('div'); col.className = 'panel-section'; col.style.cssText = ` flex: 1; background: #ffffff; padding: 8px; border-radius: 6px; display: flex; flex-direction: column; border: 1px solid #e2e8f0; `; col.innerHTML = `
โ„น๏ธ HOW IT WORKS
1. Upload files
2. Click "Scan"
3. Click file to copy
4. Paste in chat
5. Auto-monitoring
`; return col; }, createBottomButtons() { const bottomDiv = document.createElement('div'); bottomDiv.id = 'bottom-buttons'; bottomDiv.style.cssText = 'display: flex; flex-direction: row; gap: 6px; margin-top: 10px;'; const testBtn = this.createButton({ id: 'test-status-btn', text: '๐Ÿงช Test', style: ` flex: 1; padding: 8px; background: #cbd5e1; color: #1e293b; border: none; border-radius: 6px; cursor: pointer; font-size: 11px; font-weight: 600; transition: all 0.2s; display: none; `, hover: { in: { background: '#94a3b8', color: '#ffffff' }, out: { background: '#cbd5e1', color: '#1e293b' } }, onClick: () => { console.log('๐Ÿงช Test button clicked'); statusManager.update('warning', 'โš ๏ธ Test warning'); setTimeout(() => statusManager.update('safe', 'โœ… Test passed'), 2000); } }); const maximizeBtn = this.createButton({ id: 'maximize-btn', text: 'โž•', style: ` flex: 1; padding: 8px; background: #cbd5e1; color: #1e293b; border: none; border-radius: 6px; cursor: pointer; font-size: 16px; font-weight: 600; transition: all 0.2s; display: flex; align-items: center; justify-content: center; `, hover: { in: { background: '#94a3b8', color: '#ffffff' }, out: { background: '#cbd5e1', color: '#1e293b' } }, onClick: () => this.toggleMaximize() }); bottomDiv.appendChild(testBtn); bottomDiv.appendChild(maximizeBtn); return bottomDiv; } }; // ==================== PROMPT INJECTION ==================== const promptInjector = { enhance() { const observer = new MutationObserver(() => { const textareas = document.querySelectorAll('textarea, [contenteditable="true"]'); textareas.forEach(textarea => { if (textarea.dataset.workflowEnhanced) return; textarea.dataset.workflowEnhanced = 'true'; textarea.addEventListener('keydown', (e) => { if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') { const currentText = textarea.value || textarea.textContent || ''; if (!currentText.includes('downloadable file') && currentText.length > 100) { const reminder = '\n\nโš ๏ธ CRITICAL: Create downloadable file only, do NOT paste content in chat. Save to /mnt/user-data/outputs/'; if (textarea.value !== undefined) { textarea.value = currentText + reminder; } else { textarea.textContent = currentText + reminder; } } } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); console.log('โœ‰๏ธ Prompt injector active'); } }; // ==================== INITIALIZATION ==================== function init() { console.log('๐Ÿš€ Token Saver v35 initializing...'); try { dragManager.init(); uiManager.createPanel(); promptInjector.enhance(); monitoringManager.observeMessages(); fileManager.startPeriodicCheck(); fileManager.detect(); statusManager.update('safe', 'โœ… Token Saver v35 ready'); console.log('โœ… Token Saver v35 initialized successfully!'); } catch (error) { console.error('โŒ Initialization error:', error); } } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } setTimeout(() => { if (!document.getElementById('workflow-panel')) { console.log('๐Ÿ”„ Retrying initialization...'); init(); } }, 1000); })();