From 5e5e9104480726b1f6f9968fb6f319d043ffd8e0 Mon Sep 17 00:00:00 2001 From: Andy Cordero Date: Sun, 2 Mar 2025 22:33:02 -0500 Subject: [PATCH 1/2] refactor: improve code formatting and consistency in App.tsx and main.tsx --- src/App.tsx | 35 +++++++++++++++++++---------------- src/main.tsx | 12 ++++++------ 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 2bc560a..ffa538a 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,36 +1,43 @@ import './App.css'; -import { useState } from "react"; +import { useState } from 'react'; function App() { // Load initial values from localStorage - const [apiKey, setApiKey] = useState(localStorage.getItem('OPENROUTER_API_KEY') || ''); - const [selectedModel, setSelectedModel] = useState(localStorage.getItem('SELECTED_MODEL') || 'google/gemini-2.0-flash-thinking-exp:free'); - const [enabled, setEnabled] = useState(localStorage.getItem('Ai_status') === 'true'); + const [apiKey, setApiKey] = useState( + localStorage.getItem('OPENROUTER_API_KEY') || '' + ); + const [selectedModel, setSelectedModel] = useState( + localStorage.getItem('SELECTED_MODEL') || + 'google/gemini-2.0-flash-thinking-exp:free' + ); + const [enabled, setEnabled] = useState( + localStorage.getItem('Ai_status') === 'true' + ); // Function to save the API key const handleApiKeyChange = (event: React.ChangeEvent) => { setApiKey(event.target.value); - } + }; const saveApiKey = () => { localStorage.setItem('OPENROUTER_API_KEY', apiKey); - } + }; // Function to save the selected model const handleModelChange = (event: React.ChangeEvent) => { setSelectedModel(event.target.value); - } + }; const saveModel = () => { localStorage.setItem('SELECTED_MODEL', selectedModel); - } + }; // Function to handle checkbox state const handleAiToggle = () => { const newStatus = !enabled; setEnabled(newStatus); localStorage.setItem('Ai_status', newStatus.toString()); - } + }; return ( <> @@ -48,7 +55,7 @@ function App() { Save Key - +

Open Router Model

-
+
diff --git a/src/main.tsx b/src/main.tsx index bef5202..df655ea 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,10 +1,10 @@ -import { StrictMode } from 'react' -import { createRoot } from 'react-dom/client' -import './index.css' -import App from './App.tsx' +import { StrictMode } from 'react'; +import { createRoot } from 'react-dom/client'; +import './index.css'; +import App from './App.tsx'; createRoot(document.getElementById('root')!).render( - , -) + +); From 5040297387286f93d6093a16cd274a795bd554ba Mon Sep 17 00:00:00 2001 From: Andy Cordero Date: Sun, 2 Mar 2025 22:33:40 -0500 Subject: [PATCH 2/2] style: improve formatting and consistency in background.js and manifest.json --- public/background.js | 2 +- public/content.js | 329 +++++++++++++++++++++---------------------- public/manifest.json | 55 +++----- 3 files changed, 185 insertions(+), 201 deletions(-) diff --git a/public/background.js b/public/background.js index 80db332..04ba5bf 100644 --- a/public/background.js +++ b/public/background.js @@ -1,3 +1,3 @@ chrome.runtime.onInstalled.addListener(() => { - console.log("AI Autocomplete Extension Installed"); + console.log('AI Autocomplete Extension Installed'); }); diff --git a/public/content.js b/public/content.js index 3a45b46..1e4889b 100644 --- a/public/content.js +++ b/public/content.js @@ -1,218 +1,213 @@ let currentInput = null; -let suggestionText = " AI generated text"; // Placeholder suggestion -let originalText = ""; // Store user's text before suggestion +let suggestionText = ' AI generated text'; // Placeholder suggestion +let originalText = ''; // Store user's text before suggestion let suggestionStart = -1; // Position where suggestion starts -let OPENROUTER_API_KEY =localStorage.getItem('OPENROUTER_API_KEY') || ""; -let SELECTED_MODEL =localStorage.getItem('SELECTED_MODEL') || 'google/gemini-2.0-flash-thinking-exp:free'; -let AI_STATUS=localStorage.getItem('Ai_status') === 'true'; - -async function generateAISugesstion(context, userData){ - if(AI_STATUS){ - if(!OPENROUTER_API_KEY){ - console.log("Add api key!") - return +let OPENROUTER_API_KEY = localStorage.getItem('OPENROUTER_API_KEY') || ''; +let SELECTED_MODEL = + localStorage.getItem('SELECTED_MODEL') || + 'google/gemini-2.0-flash-thinking-exp:free'; +let AI_STATUS = localStorage.getItem('Ai_status') === 'true'; + +async function generateAISugesstion(context, userData) { + if (AI_STATUS) { + if (!OPENROUTER_API_KEY) { + console.log('Add api key!'); + return; } - try{ - let response = await fetch('https://openrouter.ai/api/v1/chat/completions',{ - method: 'POST', - headers:{ - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${OPENROUTER_API_KEY}`, - 'HTTP-Referer' : window.location.origin, - 'X-Title' : 'Smart fill' - }, - - body: JSON.stringify({ - model: SELECTED_MODEL, - messages: [ - { - role: 'system', - content: `Answer as quickly as possible. You are a personalized assistant helping the user complete their task of answering questions. You are provided with some context, and your task is to provide an efficient response in up to 10 words related to the provided context. If you cannot generate anything, return an empty response.` - }, - { - role: 'user', - content: `Context: ${context}\n\nText entered by user:\n${userData}` - } - ] - }) - }); - - if (!response.ok) { - throw new Error(`API request failed: ${response.statusText}`); + try { + let response = await fetch( + 'https://openrouter.ai/api/v1/chat/completions', + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${OPENROUTER_API_KEY}`, + 'HTTP-Referer': window.location.origin, + 'X-Title': 'Smart fill', + }, + + body: JSON.stringify({ + model: SELECTED_MODEL, + messages: [ + { + role: 'system', + content: `Answer as quickly as possible. You are a personalized assistant helping the user complete their task of answering questions. You are provided with some context, and your task is to provide an efficient response in up to 10 words related to the provided context. If you cannot generate anything, return an empty response.`, + }, + { + role: 'user', + content: `Context: ${context}\n\nText entered by user:\n${userData}`, + }, + ], + }), } + ); + + if (!response.ok) { + throw new Error(`API request failed: ${response.statusText}`); + } - let data = await response.json(); - console.log(data); - const suggestionText = data.choices[0].message.content; - // Handle the suggestionText, like setting it in state or displaying it - return suggestionText; + let data = await response.json(); + console.log(data); + const suggestionText = data.choices[0].message.content; + // Handle the suggestionText, like setting it in state or displaying it + return suggestionText; } catch (error) { - console.error("Error getting suggestion:", error); - } - } - else{ - console.log("As the extension is disabled not suggesting anything ") + console.error('Error getting suggestion:', error); } + } else { + console.log('As the extension is disabled not suggesting anything '); + } } function getTextFieldContext(inputElement) { - if (!inputElement) return null; + if (!inputElement) return null; - let context = ""; + let context = ''; - // 1. Check for associated label - let label = document.querySelector(`label[for="${inputElement.id}"]`); - if (label) context += `Label: ${label.innerText}. `; + // 1. Check for associated label + let label = document.querySelector(`label[for="${inputElement.id}"]`); + if (label) context += `Label: ${label.innerText}. `; - // 2. Check for placeholder - if (inputElement.placeholder) { - context += `Placeholder: ${inputElement.placeholder}. `; - } + // 2. Check for placeholder + if (inputElement.placeholder) { + context += `Placeholder: ${inputElement.placeholder}. `; + } - // 3. Check for accessibility attributes - if (inputElement.getAttribute("aria-label")) { - context += `ARIA Label: ${inputElement.getAttribute("aria-label")}. `; - } + // 3. Check for accessibility attributes + if (inputElement.getAttribute('aria-label')) { + context += `ARIA Label: ${inputElement.getAttribute('aria-label')}. `; + } - // 4. Check for nearby heading (h1, h2, h3, etc.) - let closestHeading = inputElement.closest("form, div")?.querySelector("h1, h2, h3, p"); - if (closestHeading) { - context += `Context: ${closestHeading.innerText}. `; - } + // 4. Check for nearby heading (h1, h2, h3, etc.) + let closestHeading = inputElement + .closest('form, div') + ?.querySelector('h1, h2, h3, p'); + if (closestHeading) { + context += `Context: ${closestHeading.innerText}. `; + } - return context.trim(); + return context.trim(); } function createSuggestionOverlay(textarea) { - // Remove existing overlay if any - let existingOverlay = document.getElementById("ai-suggestion-overlay"); - if (existingOverlay) existingOverlay.remove(); - - // Get textarea styles - const styles = window.getComputedStyle(textarea); - - // Create a new div overlay - let overlay = document.createElement("div"); - overlay.id = "ai-suggestion-overlay"; - overlay.textContent = textarea.value; // Start with the current text - overlay.style.position = "absolute"; - overlay.style.left = `${textarea.offsetLeft}px`; - overlay.style.top = `${textarea.offsetTop}px`; - overlay.style.width = styles.width; - overlay.style.height = styles.height; - overlay.style.font = styles.font; - overlay.style.fontSize = styles.fontSize; - overlay.style.lineHeight = styles.lineHeight; - overlay.style.padding = styles.padding; - overlay.style.border = styles.border; - overlay.style.background = "transparent"; - overlay.style.color = "gray"; // Suggestion color - overlay.style.pointerEvents = "none"; // Prevent interaction - overlay.style.whiteSpace = "pre-wrap"; // Preserve line breaks - overlay.style.overflow = "hidden"; - - // Append overlay to body - document.body.appendChild(overlay); - - // Sync overlay text with textarea - textarea.addEventListener("input", () => { - overlay.textContent = textarea.value + suggestionText; - }); - - // Remove overlay when focus is lost - textarea.addEventListener("blur", () => overlay.remove()); + // Remove existing overlay if any + let existingOverlay = document.getElementById('ai-suggestion-overlay'); + if (existingOverlay) existingOverlay.remove(); + + // Get textarea styles + const styles = window.getComputedStyle(textarea); + + // Create a new div overlay + let overlay = document.createElement('div'); + overlay.id = 'ai-suggestion-overlay'; + overlay.textContent = textarea.value; // Start with the current text + overlay.style.position = 'absolute'; + overlay.style.left = `${textarea.offsetLeft}px`; + overlay.style.top = `${textarea.offsetTop}px`; + overlay.style.width = styles.width; + overlay.style.height = styles.height; + overlay.style.font = styles.font; + overlay.style.fontSize = styles.fontSize; + overlay.style.lineHeight = styles.lineHeight; + overlay.style.padding = styles.padding; + overlay.style.border = styles.border; + overlay.style.background = 'transparent'; + overlay.style.color = 'gray'; // Suggestion color + overlay.style.pointerEvents = 'none'; // Prevent interaction + overlay.style.whiteSpace = 'pre-wrap'; // Preserve line breaks + overlay.style.overflow = 'hidden'; + + // Append overlay to body + document.body.appendChild(overlay); + + // Sync overlay text with textarea + textarea.addEventListener('input', () => { + overlay.textContent = textarea.value + suggestionText; + }); + + // Remove overlay when focus is lost + textarea.addEventListener('blur', () => overlay.remove()); } // Detect when the user focuses on an input field -document.addEventListener("input", (event) => { - if (event.target.tagName === "INPUT" || event.target.tagName === "TEXTAREA") { - createSuggestionOverlay(event.target); - if(currentInput !== event.target){ - currentInput = event.target; - originalText = currentInput.value; // Store text before suggestion - suggestionStart = -1; // Reset suggestion position - let context = getTextFieldContext(event.target); - console.log("Text Field Context:", context); - currentInput.addEventListener("keydown", handleKeyEvents); +document.addEventListener('input', (event) => { + if (event.target.tagName === 'INPUT' || event.target.tagName === 'TEXTAREA') { + createSuggestionOverlay(event.target); + if (currentInput !== event.target) { + currentInput = event.target; + originalText = currentInput.value; // Store text before suggestion + suggestionStart = -1; // Reset suggestion position + let context = getTextFieldContext(event.target); + console.log('Text Field Context:', context); + currentInput.addEventListener('keydown', handleKeyEvents); } handleTyping(); -} + } }); -document.addEventListener("focusout", () => { - discardSuggestion(); -}); - - +document.addEventListener('focusout', () => { + discardSuggestion(); +}); function handleTyping(event) { - if (!currentInput) return; + if (!currentInput) return; - let cursorPos = currentInput.selectionStart; + let cursorPos = currentInput.selectionStart; - // If a suggestion isn't already shown, add it at the current cursor position - if (suggestionStart === -1) { - suggestionStart = cursorPos; // Store where suggestion begins - originalText = currentInput.value; // Save user input before suggestion - insertSuggestion(cursorPos); - } + // If a suggestion isn't already shown, add it at the current cursor position + if (suggestionStart === -1) { + suggestionStart = cursorPos; // Store where suggestion begins + originalText = currentInput.value; // Save user input before suggestion + insertSuggestion(cursorPos); + } } - // Insert suggestion at cursor position function insertSuggestion(cursorPos) { - let textBeforeCursor = currentInput.value.substring(0, cursorPos); - let textAfterCursor = currentInput.value.substring(cursorPos); - - currentInput.value = textBeforeCursor + suggestionText + textAfterCursor; - currentInput.style.color = "gray"; // Show suggestion in gray - - // Move cursor to the end of the original text - currentInput.setSelectionRange(cursorPos, cursorPos); -} + let textBeforeCursor = currentInput.value.substring(0, cursorPos); + let textAfterCursor = currentInput.value.substring(cursorPos); + + currentInput.value = textBeforeCursor + suggestionText + textAfterCursor; + currentInput.style.color = 'gray'; // Show suggestion in gray + // Move cursor to the end of the original text + currentInput.setSelectionRange(cursorPos, cursorPos); +} // Handle Tab / Esc Key function handleKeyEvents(event) { - if (!currentInput) return; - - if (event.key === "Tab") { - event.preventDefault(); - acceptSuggestion(); - } else if (event.key === "Escape") { - event.preventDefault(); - discardSuggestion(); - } -} + if (!currentInput) return; + if (event.key === 'Tab') { + event.preventDefault(); + acceptSuggestion(); + } else if (event.key === 'Escape') { + event.preventDefault(); + discardSuggestion(); + } +} function acceptSuggestion() { - if (!currentInput) return; + if (!currentInput) return; - currentInput.style.color = "black"; // Make text black - cleanup(); + currentInput.style.color = 'black'; // Make text black + cleanup(); } - - // Discard suggestion but keep user-typed content function discardSuggestion() { - if (!currentInput || suggestionStart === -1) return; + if (!currentInput || suggestionStart === -1) return; - // Remove only the suggestion, keep typed text - currentInput.value = currentInput.value.replace(suggestionText,""); - currentInput.style.color = "black"; // Restore text color - cleanup(); + // Remove only the suggestion, keep typed text + currentInput.value = currentInput.value.replace(suggestionText, ''); + currentInput.style.color = 'black'; // Restore text color + cleanup(); } - - - - function cleanup() { - if (!currentInput) return; + if (!currentInput) return; - currentInput.removeEventListener("keydown", handleKeyEvents); - suggestionStart = -1; // Reset suggestion - currentInput = null; -} \ No newline at end of file + currentInput.removeEventListener('keydown', handleKeyEvents); + suggestionStart = -1; // Reset suggestion + currentInput = null; +} diff --git a/public/manifest.json b/public/manifest.json index 6c7d058..071065a 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -1,35 +1,24 @@ { - "manifest_version": 3, - "version": "1.0.0", - "name": "SmartFill- An AI Chrome Extension", - "icons":{ - "32":"icon32.png", - "128":"icon128.png" - }, + "manifest_version": 3, + "version": "1.0.0", + "name": "SmartFill- An AI Chrome Extension", + "icons": { + "32": "icon32.png", + "128": "icon128.png" + }, - "action":{ - "default_popup":"index.html" - }, - "permissions":[ - "scripting", - "activeTab" - ], - "host_permissions":[ - "http://*/*", - "https://*/*" - ], - "background":{ - "service_worker":"background.js" - }, - "content_scripts":[ - { - "matches" :["http://*/*", - "https://*/*"], - "js":["content.js"] - } - ] - - - - - } \ No newline at end of file + "action": { + "default_popup": "index.html" + }, + "permissions": ["scripting", "activeTab"], + "host_permissions": ["http://*/*", "https://*/*"], + "background": { + "service_worker": "background.js" + }, + "content_scripts": [ + { + "matches": ["http://*/*", "https://*/*"], + "js": ["content.js"] + } + ] +}