AI Writing Assistant - Grammar, Translation & Expansion

Enhance your writing with grammar checking, Hebrew-English translation, and contextual text expansion with Formal-Persuasive and Casual-Everyday tones

Size

54.3 KB

Version

1.1.3

Created

Oct 15, 2025

Updated

about 1 month ago

1// ==UserScript==
2// @name		AI Writing Assistant - Grammar, Translation & Expansion
3// @description		Enhance your writing with grammar checking, Hebrew-English translation, and contextual text expansion with Formal-Persuasive and Casual-Everyday tones
4// @version		1.1.3
5// @match		https://*/*
6// @match		http://*/*
7// @icon		https://cdn.oaistatic.com/assets/favicon-l4nq08hd.svg
8// ==/UserScript==
9(function() {
10    'use strict';
11
12    // ============================================
13    // CONFIGURATION & CONSTANTS
14    // ============================================
15    
16    const CONFIG = {
17        tones: {
18            FORMAL: 'Formal-Persuasive',
19            CASUAL: 'Casual-Everyday'
20        },
21        colors: {
22            background: '#FDFDFD',
23            backgroundAlt: '#F7F8FB',
24            primary: '#3B82F6',
25            textPrimary: '#111827',
26            textSecondary: '#374151',
27            error: '#EF4444',
28            success: '#10B981'
29        },
30        shortcuts: {
31            commandPalette: 'k',
32            grammar: 'g',
33            translate: 't',
34            expand: 'e'
35        }
36    };
37
38    // ============================================
39    // STATE MANAGEMENT
40    // ============================================
41    
42    let state = {
43        selectedText: '',
44        selectedElement: null,
45        currentTone: CONFIG.tones.FORMAL,
46        showExplanations: true,
47        history: [],
48        glossary: []
49    };
50
51    // ============================================
52    // UTILITY FUNCTIONS
53    // ============================================
54    
55    function detectLanguage(text) {
56        const hebrewPattern = /[\u0590-\u05FF]/;
57        return hebrewPattern.test(text) ? 'he' : 'en';
58    }
59
60    function getDefaultToneForPlatform() {
61        const url = window.location.href;
62        if (url.includes('mail.google.com') || url.includes('linkedin.com')) {
63            return CONFIG.tones.FORMAL;
64        } else if (url.includes('web.whatsapp.com')) {
65            return CONFIG.tones.CASUAL;
66        } else if (url.includes('docs.google.com')) {
67            return CONFIG.tones.FORMAL;
68        }
69        return CONFIG.tones.FORMAL;
70    }
71
72    function debounce(func, wait) {
73        let timeout;
74        return function executedFunction(...args) {
75            const later = () => {
76                clearTimeout(timeout);
77                func(...args);
78            };
79            clearTimeout(timeout);
80            timeout = setTimeout(later, wait);
81        };
82    }
83
84    async function saveToHistory(action, input, output) {
85        const historyItem = {
86            timestamp: Date.now(),
87            action,
88            input,
89            output,
90            tone: state.currentTone
91        };
92        
93        state.history.unshift(historyItem);
94        if (state.history.length > 50) {
95            state.history = state.history.slice(0, 50);
96        }
97        
98        await GM.setValue('writing_assistant_history', JSON.stringify(state.history));
99        console.log('Saved to history:', action);
100    }
101
102    async function loadState() {
103        try {
104            const savedTone = await GM.getValue('writing_assistant_tone', CONFIG.tones.FORMAL);
105            const savedExplanations = await GM.getValue('writing_assistant_explanations', true);
106            const savedHistory = await GM.getValue('writing_assistant_history', '[]');
107            const savedGlossary = await GM.getValue('writing_assistant_glossary', '[]');
108            
109            state.currentTone = savedTone;
110            state.showExplanations = savedExplanations;
111            state.history = JSON.parse(savedHistory);
112            state.glossary = JSON.parse(savedGlossary);
113            
114            console.log('State loaded:', state);
115        } catch (error) {
116            console.error('Error loading state:', error);
117        }
118    }
119
120    // ============================================
121    // AI FUNCTIONS
122    // ============================================
123    
124    async function checkGrammar(text) {
125        console.log('Checking grammar for:', text);
126        
127        const prompt = `You are an expert English grammar checker. Analyze the following text and identify ALL grammar, spelling, punctuation, syntax, and word choice errors.
128
129Text to check: "${text}"
130
131For each error found, provide:
1321. The exact error text
1332. The suggested correction
1343. A brief explanation (10-20 words)
1354. The error category (Subject-Verb Agreement, Tense Consistency, Comma Splices, Capitalization, Articles, Spelling, Punctuation, Word Choice, etc.)
136
137IMPORTANT RULES:
138- Never change the meaning of sentences
139- Preserve proper nouns, brand names, and technical terms
140- Only suggest corrections for actual errors
141- Be precise and specific`;
142
143        try {
144            const result = await RM.aiCall(prompt, {
145                type: 'json_schema',
146                json_schema: {
147                    name: 'grammar_check',
148                    schema: {
149                        type: 'object',
150                        properties: {
151                            errors: {
152                                type: 'array',
153                                items: {
154                                    type: 'object',
155                                    properties: {
156                                        errorText: { type: 'string' },
157                                        correction: { type: 'string' },
158                                        explanation: { type: 'string' },
159                                        category: { type: 'string' }
160                                    },
161                                    required: ['errorText', 'correction', 'explanation', 'category']
162                                }
163                            },
164                            correctedText: { type: 'string' }
165                        },
166                        required: ['errors', 'correctedText']
167                    }
168                }
169            });
170            
171            await saveToHistory('grammar', text, result.correctedText);
172            return result;
173        } catch (error) {
174            console.error('Grammar check error:', error);
175            throw error;
176        }
177    }
178
179    async function translateText(text, targetLang = null) {
180        const sourceLang = detectLanguage(text);
181        const target = targetLang || (sourceLang === 'he' ? 'en' : 'he');
182        
183        console.log(`Translating from ${sourceLang} to ${target}`);
184        
185        const langNames = {
186            he: 'Hebrew',
187            en: 'English'
188        };
189        
190        const prompt = `Translate the following text from ${langNames[sourceLang]} to ${langNames[target]}.
191
192Text: "${text}"
193
194Tone: ${state.currentTone}
195
196IMPORTANT RULES:
197- Maintain the ${state.currentTone} tone in the translation
198- Preserve formatting: paragraphs, lists, emojis
199- Keep proper nouns, brand names unchanged
200- Provide an accurate, natural translation
201- Do NOT expand or rewrite beyond translation`;
202
203        try {
204            const result = await RM.aiCall(prompt, {
205                type: 'json_schema',
206                json_schema: {
207                    name: 'translation',
208                    schema: {
209                        type: 'object',
210                        properties: {
211                            translatedText: { type: 'string' },
212                            sourceLang: { type: 'string' },
213                            targetLang: { type: 'string' }
214                        },
215                        required: ['translatedText', 'sourceLang', 'targetLang']
216                    }
217                }
218            });
219            
220            await saveToHistory('translate', text, result.translatedText);
221            return result;
222        } catch (error) {
223            console.error('Translation error:', error);
224            throw error;
225        }
226    }
227
228    async function expandText(text, mode = 'auto') {
229        console.log('Expanding text:', text, 'Mode:', mode);
230        
231        // Determine expansion mode
232        const wordCount = text.split(/\s+/).length;
233        const isSentence = wordCount < 20 || mode === 'sentence';
234        
235        let prompt;
236        if (isSentence) {
237            prompt = `Expand the following sentence by adding 1-2 complementary sentences that add value, clarity, or a call-to-action.
238
239Original sentence: "${text}"
240
241Tone: ${state.currentTone}
242
243Guidelines for ${state.currentTone}:
244${state.currentTone === CONFIG.tones.FORMAL ? 
245        '- Use measured, professional language\n- Active verbs\n- Clear call-to-action\n- Avoid slang' :
246        '- Conversational, friendly style\n- Short and direct\n- Casual but clear'}
247
248Provide 2 different variations.`;
249        } else {
250            prompt = `Expand the following paragraph to 80-140 words using this structure:
2511. Intent/Purpose
2522. Supporting argument
2533. Example/Value proposition
2544. ${state.currentTone === CONFIG.tones.FORMAL ? 'Professional call-to-action' : 'Direct, friendly message'}
255
256Original text: "${text}"
257
258Tone: ${state.currentTone}
259
260Guidelines for ${state.currentTone}:
261${state.currentTone === CONFIG.tones.FORMAL ? 
262        '- Professional, persuasive language\n- Structured arguments\n- Clear business value\n- Action-oriented conclusion' :
263        '- Conversational, approachable style\n- Natural flow\n- Relatable examples\n- Friendly closing'}
264
265IMPORTANT:
266- Preserve all facts and key information
267- Do NOT add false or sensitive data
268- Maintain key terms and phrases
269- Provide 2 different variations`;
270        }
271
272        try {
273            const result = await RM.aiCall(prompt, {
274                type: 'json_schema',
275                json_schema: {
276                    name: 'text_expansion',
277                    schema: {
278                        type: 'object',
279                        properties: {
280                            variations: {
281                                type: 'array',
282                                items: { type: 'string' },
283                                minItems: 2,
284                                maxItems: 2
285                            },
286                            mode: { type: 'string', enum: ['sentence', 'paragraph'] },
287                            wordCount: { type: 'number' }
288                        },
289                        required: ['variations', 'mode']
290                    }
291                }
292            });
293            
294            await saveToHistory('expand', text, result.variations[0]);
295            return result;
296        } catch (error) {
297            console.error('Expansion error:', error);
298            throw error;
299        }
300    }
301
302    // ============================================
303    // UI COMPONENTS
304    // ============================================
305    
306    function createFloatingToolbar() {
307        const toolbar = document.createElement('div');
308        toolbar.id = 'writing-assistant-toolbar';
309        toolbar.style.cssText = `
310            position: absolute;
311            display: none;
312            background: ${CONFIG.colors.background};
313            border: 1px solid #E5E7EB;
314            border-radius: 8px;
315            padding: 8px;
316            box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
317            z-index: 999999;
318            font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
319            font-size: 14px;
320            gap: 4px;
321            display: flex;
322            align-items: center;
323        `;
324        
325        const buttons = [
326            { id: 'grammar', label: 'Grammar', icon: '✓', action: handleGrammarCheck },
327            { id: 'translate', label: 'Translate', icon: '⇄', action: handleTranslate },
328            { id: 'expand', label: 'Expand', icon: '↗', action: handleExpand },
329            { id: 'tone', label: state.currentTone === CONFIG.tones.FORMAL ? 'Formal' : 'Casual', icon: '◐', action: toggleTone }
330        ];
331        
332        buttons.forEach(btn => {
333            const button = document.createElement('button');
334            button.id = `wa-btn-${btn.id}`;
335            button.innerHTML = `<span style="margin-right: 4px;">${btn.icon}</span>${btn.label}`;
336            button.style.cssText = `
337                background: white;
338                border: 1px solid #E5E7EB;
339                border-radius: 6px;
340                padding: 6px 12px;
341                cursor: pointer;
342                font-size: 13px;
343                color: ${CONFIG.colors.textPrimary};
344                transition: all 0.2s;
345                white-space: nowrap;
346            `;
347            
348            button.addEventListener('mouseenter', () => {
349                button.style.background = CONFIG.colors.backgroundAlt;
350                button.style.borderColor = CONFIG.colors.primary;
351            });
352            
353            button.addEventListener('mouseleave', () => {
354                button.style.background = 'white';
355                button.style.borderColor = '#E5E7EB';
356            });
357            
358            button.addEventListener('click', async (e) => {
359                e.preventDefault();
360                e.stopPropagation();
361                await btn.action();
362            });
363            
364            toolbar.appendChild(button);
365        });
366        
367        // More menu button
368        const moreBtn = document.createElement('button');
369        moreBtn.innerHTML = '⋯';
370        moreBtn.style.cssText = `
371            background: white;
372            border: 1px solid #E5E7EB;
373            border-radius: 6px;
374            padding: 6px 12px;
375            cursor: pointer;
376            font-size: 16px;
377            color: ${CONFIG.colors.textPrimary};
378        `;
379        moreBtn.addEventListener('click', (e) => {
380            e.preventDefault();
381            e.stopPropagation();
382            showCommandPalette();
383        });
384        toolbar.appendChild(moreBtn);
385        
386        document.body.appendChild(toolbar);
387        return toolbar;
388    }
389
390    function showToolbar(x, y) {
391        const toolbar = document.getElementById('writing-assistant-toolbar');
392        if (!toolbar) return;
393        
394        toolbar.style.display = 'flex';
395        toolbar.style.left = `${x}px`;
396        toolbar.style.top = `${y + 10}px`;
397        
398        // Update tone button label
399        const toneBtn = document.getElementById('wa-btn-tone');
400        if (toneBtn) {
401            toneBtn.innerHTML = `<span style="margin-right: 4px;"></span>${state.currentTone === CONFIG.tones.FORMAL ? 'Formal' : 'Casual'}`;
402        }
403    }
404
405    function hideToolbar() {
406        const toolbar = document.getElementById('writing-assistant-toolbar');
407        if (toolbar) {
408            toolbar.style.display = 'none';
409        }
410    }
411
412    function createResultModal(title, content, variations = null) {
413        // Remove existing modal
414        const existing = document.getElementById('writing-assistant-modal');
415        if (existing) existing.remove();
416        
417        const modal = document.createElement('div');
418        modal.id = 'writing-assistant-modal';
419        modal.style.cssText = `
420            position: fixed;
421            top: 50%;
422            left: 50%;
423            transform: translate(-50%, -50%);
424            background: ${CONFIG.colors.background};
425            border-radius: 12px;
426            padding: 24px;
427            box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
428            z-index: 1000000;
429            max-width: 600px;
430            width: 90%;
431            max-height: 80vh;
432            overflow-y: auto;
433            font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
434        `;
435        
436        const titleEl = document.createElement('h3');
437        titleEl.textContent = title;
438        titleEl.style.cssText = `
439            margin: 0 0 16px 0;
440            font-size: 18px;
441            font-weight: 600;
442            color: ${CONFIG.colors.textPrimary};
443        `;
444        modal.appendChild(titleEl);
445        
446        if (variations && Array.isArray(variations)) {
447            variations.forEach((variation, index) => {
448                const varContainer = document.createElement('div');
449                varContainer.style.cssText = `
450                    background: white;
451                    border: 1px solid #E5E7EB;
452                    border-radius: 8px;
453                    padding: 16px;
454                    margin-bottom: 12px;
455                `;
456                
457                const varLabel = document.createElement('div');
458                varLabel.textContent = `Variation ${index + 1}`;
459                varLabel.style.cssText = `
460                    font-size: 12px;
461                    font-weight: 600;
462                    color: ${CONFIG.colors.textSecondary};
463                    margin-bottom: 8px;
464                    text-transform: uppercase;
465                    letter-spacing: 0.5px;
466                `;
467                varContainer.appendChild(varLabel);
468                
469                const varText = document.createElement('div');
470                varText.textContent = variation;
471                varText.style.cssText = `
472                    color: ${CONFIG.colors.textPrimary};
473                    line-height: 1.6;
474                    margin-bottom: 12px;
475                `;
476                varContainer.appendChild(varText);
477                
478                const btnContainer = document.createElement('div');
479                btnContainer.style.cssText = 'display: flex; gap: 8px;';
480                
481                const applyBtn = document.createElement('button');
482                applyBtn.textContent = 'Apply';
483                applyBtn.style.cssText = `
484                    background: ${CONFIG.colors.primary};
485                    color: white;
486                    border: none;
487                    border-radius: 6px;
488                    padding: 8px 16px;
489                    cursor: pointer;
490                    font-size: 13px;
491                    font-weight: 500;
492                `;
493                applyBtn.addEventListener('click', () => {
494                    applyText(variation);
495                    modal.remove();
496                    removeOverlay();
497                });
498                btnContainer.appendChild(applyBtn);
499                
500                const copyBtn = document.createElement('button');
501                copyBtn.textContent = 'Copy';
502                copyBtn.style.cssText = `
503                    background: white;
504                    color: ${CONFIG.colors.textPrimary};
505                    border: 1px solid #E5E7EB;
506                    border-radius: 6px;
507                    padding: 8px 16px;
508                    cursor: pointer;
509                    font-size: 13px;
510                `;
511                copyBtn.addEventListener('click', async () => {
512                    await GM.setClipboard(variation);
513                    copyBtn.textContent = 'Copied!';
514                    setTimeout(() => copyBtn.textContent = 'Copy', 2000);
515                });
516                btnContainer.appendChild(copyBtn);
517                
518                varContainer.appendChild(btnContainer);
519                modal.appendChild(varContainer);
520            });
521        } else {
522            const contentEl = document.createElement('div');
523            contentEl.style.cssText = `
524                background: white;
525                border: 1px solid #E5E7EB;
526                border-radius: 8px;
527                padding: 16px;
528                color: ${CONFIG.colors.textPrimary};
529                line-height: 1.6;
530                margin-bottom: 16px;
531            `;
532            contentEl.textContent = content;
533            modal.appendChild(contentEl);
534            
535            const btnContainer = document.createElement('div');
536            btnContainer.style.cssText = 'display: flex; gap: 8px; justify-content: flex-end;';
537            
538            const applyBtn = document.createElement('button');
539            applyBtn.textContent = 'Apply';
540            applyBtn.style.cssText = `
541                background: ${CONFIG.colors.primary};
542                color: white;
543                border: none;
544                border-radius: 6px;
545                padding: 8px 16px;
546                cursor: pointer;
547                font-size: 13px;
548                font-weight: 500;
549            `;
550            applyBtn.addEventListener('click', () => {
551                applyText(content);
552                modal.remove();
553                removeOverlay();
554            });
555            btnContainer.appendChild(applyBtn);
556            
557            const copyBtn = document.createElement('button');
558            copyBtn.textContent = 'Copy';
559            copyBtn.style.cssText = `
560                background: white;
561                color: ${CONFIG.colors.textPrimary};
562                border: 1px solid #E5E7EB;
563                border-radius: 6px;
564                padding: 8px 16px;
565                cursor: pointer;
566                font-size: 13px;
567            `;
568            copyBtn.addEventListener('click', async () => {
569                await GM.setClipboard(content);
570                copyBtn.textContent = 'Copied!';
571                setTimeout(() => copyBtn.textContent = 'Copy', 2000);
572            });
573            btnContainer.appendChild(copyBtn);
574            
575            modal.appendChild(btnContainer);
576        }
577        
578        const closeBtn = document.createElement('button');
579        closeBtn.textContent = '×';
580        closeBtn.style.cssText = `
581            position: absolute;
582            top: 16px;
583            right: 16px;
584            background: none;
585            border: none;
586            font-size: 24px;
587            cursor: pointer;
588            color: ${CONFIG.colors.textSecondary};
589            width: 32px;
590            height: 32px;
591            display: flex;
592            align-items: center;
593            justify-content: center;
594            border-radius: 6px;
595        `;
596        closeBtn.addEventListener('click', () => {
597            modal.remove();
598            removeOverlay();
599        });
600        modal.appendChild(closeBtn);
601        
602        createOverlay();
603        document.body.appendChild(modal);
604    }
605
606    function createOverlay() {
607        const existing = document.getElementById('writing-assistant-overlay');
608        if (existing) return;
609        
610        const overlay = document.createElement('div');
611        overlay.id = 'writing-assistant-overlay';
612        overlay.style.cssText = `
613            position: fixed;
614            top: 0;
615            left: 0;
616            right: 0;
617            bottom: 0;
618            background: rgba(0, 0, 0, 0.5);
619            z-index: 999999;
620        `;
621        overlay.addEventListener('click', () => {
622            const modal = document.getElementById('writing-assistant-modal');
623            if (modal) modal.remove();
624            overlay.remove();
625        });
626        document.body.appendChild(overlay);
627    }
628
629    function removeOverlay() {
630        const overlay = document.getElementById('writing-assistant-overlay');
631        if (overlay) overlay.remove();
632    }
633
634    function showLoadingIndicator(message = 'Processing...') {
635        const existing = document.getElementById('writing-assistant-loading');
636        if (existing) existing.remove();
637        
638        const loader = document.createElement('div');
639        loader.id = 'writing-assistant-loading';
640        loader.style.cssText = `
641            position: fixed;
642            top: 20px;
643            right: 20px;
644            background: ${CONFIG.colors.primary};
645            color: white;
646            padding: 12px 20px;
647            border-radius: 8px;
648            box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
649            z-index: 1000001;
650            font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
651            font-size: 14px;
652            display: flex;
653            align-items: center;
654            gap: 10px;
655        `;
656        
657        const spinner = document.createElement('div');
658        spinner.style.cssText = `
659            width: 16px;
660            height: 16px;
661            border: 2px solid rgba(255, 255, 255, 0.3);
662            border-top-color: white;
663            border-radius: 50%;
664            animation: spin 0.8s linear infinite;
665        `;
666        
667        const text = document.createElement('span');
668        text.textContent = message;
669        
670        loader.appendChild(spinner);
671        loader.appendChild(text);
672        document.body.appendChild(loader);
673        
674        // Add animation
675        if (!document.getElementById('writing-assistant-spin-style')) {
676            const style = document.createElement('style');
677            style.id = 'writing-assistant-spin-style';
678            style.textContent = `
679                @keyframes spin {
680                    to { transform: rotate(360deg); }
681                }
682            `;
683            document.head.appendChild(style);
684        }
685    }
686
687    function hideLoadingIndicator() {
688        const loader = document.getElementById('writing-assistant-loading');
689        if (loader) loader.remove();
690    }
691
692    function showCommandPalette() {
693        const existing = document.getElementById('writing-assistant-command-palette');
694        if (existing) {
695            existing.remove();
696            removeOverlay();
697            return;
698        }
699        
700        const palette = document.createElement('div');
701        palette.id = 'writing-assistant-command-palette';
702        palette.style.cssText = `
703            position: fixed;
704            top: 20%;
705            left: 50%;
706            transform: translateX(-50%);
707            background: ${CONFIG.colors.background};
708            border-radius: 12px;
709            padding: 16px;
710            box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1);
711            z-index: 1000000;
712            width: 500px;
713            max-width: 90%;
714            font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
715        `;
716        
717        const searchInput = document.createElement('input');
718        searchInput.type = 'text';
719        searchInput.placeholder = 'Type a command...';
720        searchInput.style.cssText = `
721            width: 100%;
722            padding: 12px;
723            border: 1px solid #E5E7EB;
724            border-radius: 8px;
725            font-size: 14px;
726            margin-bottom: 12px;
727            box-sizing: border-box;
728        `;
729        palette.appendChild(searchInput);
730        
731        const commands = [
732            { label: 'Check Grammar', shortcut: 'Ctrl+G', action: handleGrammarCheck },
733            { label: 'Translate Text', shortcut: 'Ctrl+T', action: handleTranslate },
734            { label: 'Expand Text', shortcut: 'Ctrl+E', action: handleExpand },
735            { label: 'Toggle Tone (Formal ⇄ Casual)', shortcut: '', action: toggleTone },
736            { label: 'Toggle Explanations', shortcut: '', action: toggleExplanations },
737            { label: 'View History', shortcut: '', action: showHistory }
738        ];
739        
740        const commandList = document.createElement('div');
741        commandList.style.cssText = 'max-height: 400px; overflow-y: auto;';
742        
743        function renderCommands(filter = '') {
744            commandList.innerHTML = '';
745            const filtered = commands.filter(cmd => 
746                cmd.label.toLowerCase().includes(filter.toLowerCase())
747            );
748            
749            filtered.forEach(cmd => {
750                const cmdEl = document.createElement('div');
751                cmdEl.style.cssText = `
752                    padding: 12px;
753                    border-radius: 6px;
754                    cursor: pointer;
755                    display: flex;
756                    justify-content: space-between;
757                    align-items: center;
758                    margin-bottom: 4px;
759                `;
760                
761                cmdEl.addEventListener('mouseenter', () => {
762                    cmdEl.style.background = CONFIG.colors.backgroundAlt;
763                });
764                
765                cmdEl.addEventListener('mouseleave', () => {
766                    cmdEl.style.background = 'transparent';
767                });
768                
769                cmdEl.addEventListener('click', async () => {
770                    palette.remove();
771                    removeOverlay();
772                    await cmd.action();
773                });
774                
775                const label = document.createElement('span');
776                label.textContent = cmd.label;
777                label.style.color = CONFIG.colors.textPrimary;
778                cmdEl.appendChild(label);
779                
780                if (cmd.shortcut) {
781                    const shortcut = document.createElement('span');
782                    shortcut.textContent = cmd.shortcut;
783                    shortcut.style.cssText = `
784                        color: ${CONFIG.colors.textSecondary};
785                        font-size: 12px;
786                        background: ${CONFIG.colors.backgroundAlt};
787                        padding: 4px 8px;
788                        border-radius: 4px;
789                    `;
790                    cmdEl.appendChild(shortcut);
791                }
792                
793                commandList.appendChild(cmdEl);
794            });
795        }
796        
797        renderCommands();
798        
799        searchInput.addEventListener('input', (e) => {
800            renderCommands(e.target.value);
801        });
802        
803        palette.appendChild(commandList);
804        
805        createOverlay();
806        document.body.appendChild(palette);
807        searchInput.focus();
808    }
809
810    // ============================================
811    // ACTION HANDLERS
812    // ============================================
813    
814    async function handleGrammarCheck() {
815        if (!state.selectedText) {
816            console.log('No text selected for grammar check');
817            return;
818        }
819        
820        const lang = detectLanguage(state.selectedText);
821        if (lang !== 'en') {
822            createResultModal('Grammar Check', 'Grammar checking is only available for English text.');
823            return;
824        }
825        
826        try {
827            showLoadingIndicator('Checking grammar...');
828            const result = await checkGrammar(state.selectedText);
829            hideLoadingIndicator();
830            
831            if (result.errors.length === 0) {
832                createResultModal('Grammar Check', 'No errors found! Your text looks great.');
833            } else {
834                // Apply inline highlighting
835                applyInlineGrammarHighlighting(result);
836            }
837        } catch (error) {
838            hideLoadingIndicator();
839            console.error('Grammar check failed:', error);
840            createResultModal('Error', 'Failed to check grammar. Please try again.');
841        }
842    }
843
844    function applyInlineGrammarHighlighting(result) {
845        if (!state.selectedElement) {
846            console.log('No element to apply highlighting to');
847            createGrammarResultModal(result);
848            return;
849        }
850        
851        const element = state.selectedElement;
852        let originalText = state.selectedText;
853        
854        // Check if element supports rich content
855        const supportsRichContent = element.isContentEditable || 
856                                    element.tagName === 'TEXTAREA' || 
857                                    element.tagName === 'INPUT';
858        
859        if (!supportsRichContent) {
860            // Fallback to modal view
861            createGrammarResultModal(result);
862            return;
863        }
864        
865        // For contenteditable elements, we can add inline highlighting
866        if (element.isContentEditable) {
867            applyContentEditableHighlighting(element, result);
868        } else {
869            // For textarea/input, show modal with inline preview
870            createGrammarResultModal(result);
871        }
872    }
873    
874    function applyContentEditableHighlighting(element, result) {
875        // Create a wrapper to show highlighted text
876        const selection = window.getSelection();
877        if (selection.rangeCount === 0) {
878            createGrammarResultModal(result);
879            return;
880        }
881        
882        const range = selection.getRangeAt(0);
883        const originalText = range.toString();
884        
885        // Build highlighted HTML
886        let highlightedHTML = originalText;
887        let offset = 0;
888        
889        // Sort errors by position in text
890        const sortedErrors = result.errors.sort((a, b) => {
891            const posA = originalText.indexOf(a.errorText);
892            const posB = originalText.indexOf(b.errorText);
893            return posA - posB;
894        });
895        
896        sortedErrors.forEach((error, index) => {
897            const errorPos = highlightedHTML.indexOf(error.errorText, offset);
898            if (errorPos !== -1) {
899                const errorId = `grammar-error-${Date.now()}-${index}`;
900                const before = highlightedHTML.substring(0, errorPos);
901                const after = highlightedHTML.substring(errorPos + error.errorText.length);
902                
903                const errorSpan = `<span class="grammar-error" data-error-id="${errorId}" style="text-decoration: underline wavy ${CONFIG.colors.error}; cursor: pointer; background-color: rgba(239, 68, 68, 0.1);" title="${error.category}: ${error.explanation}">${error.errorText}</span>`;
904                
905                highlightedHTML = before + errorSpan + after;
906                offset = errorPos + errorSpan.length;
907                
908                // Store error data for click handling
909                setTimeout(() => {
910                    const errorElement = document.querySelector(`[data-error-id="${errorId}"]`);
911                    if (errorElement) {
912                        errorElement.addEventListener('click', (e) => {
913                            e.preventDefault();
914                            e.stopPropagation();
915                            showGrammarErrorPopup(e.target, error, result.correctedText);
916                        });
917                    }
918                }, 100);
919            }
920        });
921        
922        // Replace selected text with highlighted version
923        range.deleteContents();
924        const tempDiv = document.createElement('div');
925        tempDiv.innerHTML = highlightedHTML;
926        
927        while (tempDiv.firstChild) {
928            range.insertNode(tempDiv.lastChild);
929        }
930        
931        // Show summary notification
932        showNotification(`Found ${result.errors.length} grammar issue${result.errors.length !== 1 ? 's' : ''}. Click on underlined text to fix.`);
933        
934        // Also show the full modal for reference
935        setTimeout(() => {
936            createGrammarResultModal(result);
937        }, 500);
938    }
939    
940    function showGrammarErrorPopup(targetElement, error, correctedFullText) {
941        // Remove existing popup
942        const existingPopup = document.getElementById('grammar-error-popup');
943        if (existingPopup) existingPopup.remove();
944        
945        const popup = document.createElement('div');
946        popup.id = 'grammar-error-popup';
947        
948        const rect = targetElement.getBoundingClientRect();
949        popup.style.cssText = `
950            position: fixed;
951            left: ${rect.left}px;
952            top: ${rect.bottom + 5}px;
953            background: white;
954            border: 1px solid ${CONFIG.colors.error};
955            border-radius: 8px;
956            padding: 12px;
957            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
958            z-index: 1000000;
959            max-width: 300px;
960            font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
961        `;
962        
963        const category = document.createElement('div');
964        category.textContent = error.category;
965        category.style.cssText = `
966            font-size: 10px;
967            font-weight: 600;
968            color: ${CONFIG.colors.error};
969            text-transform: uppercase;
970            margin-bottom: 6px;
971            letter-spacing: 0.5px;
972        `;
973        popup.appendChild(category);
974        
975        const errorText = document.createElement('div');
976        errorText.innerHTML = `<span style="text-decoration: line-through; color: ${CONFIG.colors.error};">${error.errorText}</span><span style="color: ${CONFIG.colors.success}; font-weight: 500;">${error.correction}</span>`;
977        errorText.style.cssText = `
978            margin-bottom: 8px;
979            font-size: 13px;
980        `;
981        popup.appendChild(errorText);
982        
983        if (state.showExplanations) {
984            const explanation = document.createElement('div');
985            explanation.textContent = error.explanation;
986            explanation.style.cssText = `
987                font-size: 12px;
988                color: ${CONFIG.colors.textSecondary};
989                margin-bottom: 10px;
990                font-style: italic;
991            `;
992            popup.appendChild(explanation);
993        }
994        
995        const btnContainer = document.createElement('div');
996        btnContainer.style.cssText = 'display: flex; gap: 6px;';
997        
998        const fixBtn = document.createElement('button');
999        fixBtn.textContent = 'Fix';
1000        fixBtn.style.cssText = `
1001            background: ${CONFIG.colors.primary};
1002            color: white;
1003            border: none;
1004            border-radius: 4px;
1005            padding: 6px 12px;
1006            cursor: pointer;
1007            font-size: 12px;
1008            font-weight: 500;
1009        `;
1010        fixBtn.addEventListener('click', () => {
1011            targetElement.textContent = error.correction;
1012            targetElement.style.textDecoration = 'none';
1013            targetElement.style.backgroundColor = 'transparent';
1014            targetElement.classList.remove('grammar-error');
1015            popup.remove();
1016            showNotification('Error fixed!');
1017        });
1018        btnContainer.appendChild(fixBtn);
1019        
1020        const ignoreBtn = document.createElement('button');
1021        ignoreBtn.textContent = 'Ignore';
1022        ignoreBtn.style.cssText = `
1023            background: white;
1024            color: ${CONFIG.colors.textPrimary};
1025            border: 1px solid #E5E7EB;
1026            border-radius: 4px;
1027            padding: 6px 12px;
1028            cursor: pointer;
1029            font-size: 12px;
1030        `;
1031        ignoreBtn.addEventListener('click', () => {
1032            targetElement.style.textDecoration = 'none';
1033            targetElement.style.backgroundColor = 'transparent';
1034            targetElement.classList.remove('grammar-error');
1035            popup.remove();
1036        });
1037        btnContainer.appendChild(ignoreBtn);
1038        
1039        popup.appendChild(btnContainer);
1040        
1041        document.body.appendChild(popup);
1042        
1043        // Close popup when clicking outside
1044        setTimeout(() => {
1045            document.addEventListener('click', function closePopup(e) {
1046                if (!popup.contains(e.target) && e.target !== targetElement) {
1047                    popup.remove();
1048                    document.removeEventListener('click', closePopup);
1049                }
1050            });
1051        }, 100);
1052    }
1053
1054    function createGrammarResultModal(result) {
1055        const existing = document.getElementById('writing-assistant-modal');
1056        if (existing) existing.remove();
1057        
1058        const modal = document.createElement('div');
1059        modal.id = 'writing-assistant-modal';
1060        modal.style.cssText = `
1061            position: fixed;
1062            top: 50%;
1063            left: 50%;
1064            transform: translate(-50%, -50%);
1065            background: ${CONFIG.colors.background};
1066            border-radius: 12px;
1067            padding: 24px;
1068            box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1);
1069            z-index: 1000000;
1070            max-width: 700px;
1071            width: 90%;
1072            max-height: 80vh;
1073            overflow-y: auto;
1074            font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
1075        `;
1076        
1077        const title = document.createElement('h3');
1078        title.textContent = `Grammar Check - ${result.errors.length} issue${result.errors.length !== 1 ? 's' : ''} found`;
1079        title.style.cssText = `
1080            margin: 0 0 16px 0;
1081            font-size: 18px;
1082            font-weight: 600;
1083            color: ${CONFIG.colors.textPrimary};
1084        `;
1085        modal.appendChild(title);
1086        
1087        result.errors.forEach((error, index) => {
1088            const errorEl = document.createElement('div');
1089            errorEl.style.cssText = `
1090                background: white;
1091                border: 1px solid #E5E7EB;
1092                border-left: 3px solid ${CONFIG.colors.error};
1093                border-radius: 8px;
1094                padding: 16px;
1095                margin-bottom: 12px;
1096            `;
1097            
1098            const category = document.createElement('div');
1099            category.textContent = error.category;
1100            category.style.cssText = `
1101                font-size: 11px;
1102                font-weight: 600;
1103                color: ${CONFIG.colors.error};
1104                margin-bottom: 8px;
1105                text-transform: uppercase;
1106                letter-spacing: 0.5px;
1107            `;
1108            errorEl.appendChild(category);
1109            
1110            const errorText = document.createElement('div');
1111            errorText.innerHTML = `<span style="text-decoration: line-through; color: ${CONFIG.colors.error};">${error.errorText}</span><span style="color: ${CONFIG.colors.success}; font-weight: 500;">${error.correction}</span>`;
1112            errorText.style.cssText = `
1113                margin-bottom: 8px;
1114                font-size: 14px;
1115            `;
1116            errorEl.appendChild(errorText);
1117            
1118            if (state.showExplanations) {
1119                const explanation = document.createElement('div');
1120                explanation.textContent = error.explanation;
1121                explanation.style.cssText = `
1122                    font-size: 13px;
1123                    color: ${CONFIG.colors.textSecondary};
1124                    font-style: italic;
1125                `;
1126                errorEl.appendChild(explanation);
1127            }
1128            
1129            modal.appendChild(errorEl);
1130        });
1131        
1132        const btnContainer = document.createElement('div');
1133        btnContainer.style.cssText = 'display: flex; gap: 8px; justify-content: flex-end; margin-top: 16px;';
1134        
1135        const applyAllBtn = document.createElement('button');
1136        applyAllBtn.textContent = 'Apply All';
1137        applyAllBtn.style.cssText = `
1138            background: ${CONFIG.colors.primary};
1139            color: white;
1140            border: none;
1141            border-radius: 6px;
1142            padding: 10px 20px;
1143            cursor: pointer;
1144            font-size: 14px;
1145            font-weight: 500;
1146        `;
1147        applyAllBtn.addEventListener('click', () => {
1148            applyText(result.correctedText);
1149            modal.remove();
1150            removeOverlay();
1151        });
1152        btnContainer.appendChild(applyAllBtn);
1153        
1154        const copyBtn = document.createElement('button');
1155        copyBtn.textContent = 'Copy Corrected';
1156        copyBtn.style.cssText = `
1157            background: white;
1158            color: ${CONFIG.colors.textPrimary};
1159            border: 1px solid #E5E7EB;
1160            border-radius: 6px;
1161            padding: 10px 20px;
1162            cursor: pointer;
1163            font-size: 14px;
1164        `;
1165        copyBtn.addEventListener('click', async () => {
1166            await GM.setClipboard(result.correctedText);
1167            copyBtn.textContent = 'Copied!';
1168            setTimeout(() => copyBtn.textContent = 'Copy Corrected', 2000);
1169        });
1170        btnContainer.appendChild(copyBtn);
1171        
1172        modal.appendChild(btnContainer);
1173        
1174        const closeBtn = document.createElement('button');
1175        closeBtn.textContent = '×';
1176        closeBtn.style.cssText = `
1177            position: absolute;
1178            top: 16px;
1179            right: 16px;
1180            background: none;
1181            border: none;
1182            font-size: 24px;
1183            cursor: pointer;
1184            color: ${CONFIG.colors.textSecondary};
1185        `;
1186        closeBtn.addEventListener('click', () => {
1187            modal.remove();
1188            removeOverlay();
1189        });
1190        modal.appendChild(closeBtn);
1191        
1192        createOverlay();
1193        document.body.appendChild(modal);
1194    }
1195
1196    async function handleTranslate() {
1197        if (!state.selectedText) {
1198            console.log('No text selected for translation');
1199            return;
1200        }
1201        
1202        try {
1203            showLoadingIndicator('Translating...');
1204            const result = await translateText(state.selectedText);
1205            hideLoadingIndicator();
1206            
1207            createResultModal('Translation', result.translatedText);
1208        } catch (error) {
1209            hideLoadingIndicator();
1210            console.error('Translation failed:', error);
1211            createResultModal('Error', 'Failed to translate. Please try again.');
1212        }
1213    }
1214
1215    async function handleExpand() {
1216        if (!state.selectedText) {
1217            console.log('No text selected for expansion');
1218            return;
1219        }
1220        
1221        try {
1222            showLoadingIndicator('Expanding text...');
1223            const result = await expandText(state.selectedText);
1224            hideLoadingIndicator();
1225            
1226            createResultModal('Expanded Text', null, result.variations);
1227        } catch (error) {
1228            hideLoadingIndicator();
1229            console.error('Expansion failed:', error);
1230            createResultModal('Error', 'Failed to expand text. Please try again.');
1231        }
1232    }
1233
1234    async function toggleTone() {
1235        state.currentTone = state.currentTone === CONFIG.tones.FORMAL ? CONFIG.tones.CASUAL : CONFIG.tones.FORMAL;
1236        await GM.setValue('writing_assistant_tone', state.currentTone);
1237        
1238        console.log('Tone switched to:', state.currentTone);
1239        
1240        // Update toolbar button
1241        const toneBtn = document.getElementById('wa-btn-tone');
1242        if (toneBtn) {
1243            toneBtn.innerHTML = `<span style="margin-right: 4px;"></span>${state.currentTone === CONFIG.tones.FORMAL ? 'Formal' : 'Casual'}`;
1244        }
1245        
1246        // Show notification
1247        showNotification(`Tone: ${state.currentTone}`);
1248    }
1249
1250    async function toggleExplanations() {
1251        state.showExplanations = !state.showExplanations;
1252        await GM.setValue('writing_assistant_explanations', state.showExplanations);
1253        
1254        showNotification(`Explanations: ${state.showExplanations ? 'On' : 'Off'}`);
1255    }
1256
1257    function showHistory() {
1258        if (state.history.length === 0) {
1259            createResultModal('History', 'No history yet. Start using the assistant!');
1260            return;
1261        }
1262        
1263        const existing = document.getElementById('writing-assistant-modal');
1264        if (existing) existing.remove();
1265        
1266        const modal = document.createElement('div');
1267        modal.id = 'writing-assistant-modal';
1268        modal.style.cssText = `
1269            position: fixed;
1270            top: 50%;
1271            left: 50%;
1272            transform: translate(-50%, -50%);
1273            background: ${CONFIG.colors.background};
1274            border-radius: 12px;
1275            padding: 24px;
1276            box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1);
1277            z-index: 1000000;
1278            max-width: 700px;
1279            width: 90%;
1280            max-height: 80vh;
1281            overflow-y: auto;
1282            font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
1283        `;
1284        
1285        const title = document.createElement('h3');
1286        title.textContent = 'History';
1287        title.style.cssText = `
1288            margin: 0 0 16px 0;
1289            font-size: 18px;
1290            font-weight: 600;
1291            color: ${CONFIG.colors.textPrimary};
1292        `;
1293        modal.appendChild(title);
1294        
1295        state.history.slice(0, 20).forEach(item => {
1296            const historyItem = document.createElement('div');
1297            historyItem.style.cssText = `
1298                background: white;
1299                border: 1px solid #E5E7EB;
1300                border-radius: 8px;
1301                padding: 12px;
1302                margin-bottom: 8px;
1303            `;
1304            
1305            const header = document.createElement('div');
1306            header.style.cssText = 'display: flex; justify-content: space-between; margin-bottom: 8px;';
1307            
1308            const action = document.createElement('span');
1309            action.textContent = item.action.toUpperCase();
1310            action.style.cssText = `
1311                font-size: 11px;
1312                font-weight: 600;
1313                color: ${CONFIG.colors.primary};
1314            `;
1315            header.appendChild(action);
1316            
1317            const time = document.createElement('span');
1318            time.textContent = new Date(item.timestamp).toLocaleString();
1319            time.style.cssText = `
1320                font-size: 11px;
1321                color: ${CONFIG.colors.textSecondary};
1322            `;
1323            header.appendChild(time);
1324            
1325            historyItem.appendChild(header);
1326            
1327            const output = document.createElement('div');
1328            output.textContent = item.output.substring(0, 100) + (item.output.length > 100 ? '...' : '');
1329            output.style.cssText = `
1330                font-size: 13px;
1331                color: ${CONFIG.colors.textPrimary};
1332            `;
1333            historyItem.appendChild(output);
1334            
1335            modal.appendChild(historyItem);
1336        });
1337        
1338        const closeBtn = document.createElement('button');
1339        closeBtn.textContent = '×';
1340        closeBtn.style.cssText = `
1341            position: absolute;
1342            top: 16px;
1343            right: 16px;
1344            background: none;
1345            border: none;
1346            font-size: 24px;
1347            cursor: pointer;
1348            color: ${CONFIG.colors.textSecondary};
1349        `;
1350        closeBtn.addEventListener('click', () => {
1351            modal.remove();
1352            removeOverlay();
1353        });
1354        modal.appendChild(closeBtn);
1355        
1356        createOverlay();
1357        document.body.appendChild(modal);
1358    }
1359
1360    function showNotification(message) {
1361        const notification = document.createElement('div');
1362        notification.style.cssText = `
1363            position: fixed;
1364            bottom: 20px;
1365            right: 20px;
1366            background: ${CONFIG.colors.textPrimary};
1367            color: white;
1368            padding: 12px 20px;
1369            border-radius: 8px;
1370            box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
1371            z-index: 1000001;
1372            font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
1373            font-size: 14px;
1374        `;
1375        notification.textContent = message;
1376        document.body.appendChild(notification);
1377        
1378        setTimeout(() => {
1379            notification.remove();
1380        }, 2000);
1381    }
1382
1383    function applyText(text) {
1384        if (!state.selectedElement) {
1385            console.log('No element to apply text to');
1386            return;
1387        }
1388        
1389        const element = state.selectedElement;
1390        
1391        // Handle different input types
1392        if (element.tagName === 'TEXTAREA' || element.tagName === 'INPUT') {
1393            element.value = text;
1394            element.dispatchEvent(new Event('input', { bubbles: true }));
1395        } else if (element.isContentEditable) {
1396            // For contenteditable elements
1397            const selection = window.getSelection();
1398            if (selection.rangeCount > 0) {
1399                const range = selection.getRangeAt(0);
1400                range.deleteContents();
1401                range.insertNode(document.createTextNode(text));
1402            } else {
1403                element.textContent = text;
1404            }
1405            element.dispatchEvent(new Event('input', { bubbles: true }));
1406        }
1407        
1408        console.log('Text applied successfully');
1409        showNotification('Text applied!');
1410    }
1411
1412    // ============================================
1413    // EVENT LISTENERS
1414    // ============================================
1415    
1416    function setupEventListeners() {
1417        // Text selection handler
1418        document.addEventListener('mouseup', debounce((e) => {
1419            const selection = window.getSelection();
1420            const selectedText = selection.toString().trim();
1421            
1422            if (selectedText.length > 0) {
1423                state.selectedText = selectedText;
1424                state.selectedElement = e.target;
1425                
1426                const range = selection.getRangeAt(0);
1427                const rect = range.getBoundingClientRect();
1428                
1429                showToolbar(rect.left + rect.width / 2 - 150, rect.bottom + window.scrollY);
1430                console.log('Text selected:', selectedText);
1431            } else {
1432                hideToolbar();
1433            }
1434        }, 200));
1435        
1436        // Click outside to hide toolbar
1437        document.addEventListener('mousedown', (e) => {
1438            const toolbar = document.getElementById('writing-assistant-toolbar');
1439            if (toolbar && !toolbar.contains(e.target)) {
1440                const selection = window.getSelection();
1441                if (!selection.toString().trim()) {
1442                    hideToolbar();
1443                }
1444            }
1445        });
1446        
1447        // Keyboard shortcuts
1448        document.addEventListener('keydown', async (e) => {
1449            // Command Palette: Ctrl/Cmd + K
1450            if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
1451                e.preventDefault();
1452                showCommandPalette();
1453                return;
1454            }
1455            
1456            // Only process other shortcuts if text is selected
1457            if (!state.selectedText) return;
1458            
1459            // Grammar: Ctrl/Cmd + G
1460            if ((e.ctrlKey || e.metaKey) && e.key === 'g') {
1461                e.preventDefault();
1462                await handleGrammarCheck();
1463            }
1464            
1465            // Translate: Ctrl/Cmd + T
1466            if ((e.ctrlKey || e.metaKey) && e.key === 't') {
1467                e.preventDefault();
1468                await handleTranslate();
1469            }
1470            
1471            // Expand: Ctrl/Cmd + E
1472            if ((e.ctrlKey || e.metaKey) && e.key === 'e') {
1473                e.preventDefault();
1474                await handleExpand();
1475            }
1476        });
1477        
1478        console.log('Event listeners setup complete');
1479    }
1480
1481    // ============================================
1482    // INITIALIZATION
1483    // ============================================
1484    
1485    async function init() {
1486        console.log('AI Writing Assistant initializing...');
1487        
1488        // Load saved state
1489        await loadState();
1490        
1491        // Set default tone based on platform
1492        if (!await GM.getValue('writing_assistant_tone')) {
1493            state.currentTone = getDefaultToneForPlatform();
1494            await GM.setValue('writing_assistant_tone', state.currentTone);
1495        }
1496        
1497        // Create UI components
1498        createFloatingToolbar();
1499        
1500        // Setup event listeners
1501        setupEventListeners();
1502        
1503        console.log('AI Writing Assistant ready!');
1504        console.log('Current tone:', state.currentTone);
1505        console.log('Shortcuts: Ctrl/Cmd+K (Command Palette), Ctrl/Cmd+G (Grammar), Ctrl/Cmd+T (Translate), Ctrl/Cmd+E (Expand)');
1506    }
1507
1508    // Start the extension
1509    if (document.readyState === 'loading') {
1510        document.addEventListener('DOMContentLoaded', init);
1511    } else {
1512        init();
1513    }
1514
1515})();
AI Writing Assistant - Grammar, Translation & Expansion | Robomonkey