Greasy Fork

Greasy Fork is available in English.

Claude Token Saver v35

Prevents token waste by enforcing file creation instead of chat pasting. Monitors output length and detects file creation. FIXED: Copy command, monitoring, visual feedback.

当前为 2025-11-09 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==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
// ==/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 = `
                <div style="font-weight: 700; margin-bottom: 10px; color: #06b6d4;">📋 Copy This Command</div>
                <textarea readonly style="width: 100%; height: 150px; padding: 10px; font-family: monospace; font-size: 11px; border: 1px solid #e2e8f0; border-radius: 6px;">${text}</textarea>
                <button id="close-modal" style="margin-top: 10px; padding: 8px 16px; background: #06b6d4; color: white; border: none; border-radius: 6px; cursor: pointer; font-weight: 600;">Close</button>
            `;

            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 = `<span style="flex: 1;">${config.icon} ${message}</span>`;
            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 = `
                    <div style="color: #64748b; font-size: 10px;">
                        No files detected<br>
                        <small style="opacity: 0.7;">Upload via Claude</small>
                    </div>
                `;
                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 = `
                <div style="font-weight: 600; color: #06b6d4; margin-bottom: 2px;">📄 ${fileName}</div>
                <div style="color: #64748b; font-size: 9px;">Click to copy command</div>
            `;

            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 = `
                <div style="font-weight: 700; margin-bottom: 6px; font-size: 11px; color: #06b6d4;">💾 TOKEN SAVING</div>
                <div style="font-size: 10px; line-height: 1.6; color: #334155;">
                    ✓ Prevents pasting<br>
                    ✓ Enforces files<br>
                    ✓ Length monitor<br>
                    ✓ Timeout prevention
                </div>
            `;
            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 = `
                <div style="font-weight: 700; margin-bottom: 6px; font-size: 10px; color: #06b6d4;">📂 DETECTED FILES</div>
                <div id="file-list">
                    <div style="color: #64748b; font-size: 10px;">
                        No files detected<br>
                        <small style="opacity: 0.7;">Upload via Claude</small>
                    </div>
                </div>
            `;
            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 = `
                <div style="font-weight: 700; margin-bottom: 5px; font-size: 10px; color: #06b6d4;">ℹ️ HOW IT WORKS</div>
                <div style="font-size: 9px; line-height: 1.5; color: #64748b;">
                    1. Upload files<br>
                    2. Click "Scan"<br>
                    3. Click file to copy<br>
                    4. Paste in chat<br>
                    5. Auto-monitoring
                </div>
            `;
            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);

})();