diff --git a/apps/web/src/app/token/page.tsx b/apps/web/src/app/token/page.tsx new file mode 100644 index 0000000..833f6e4 --- /dev/null +++ b/apps/web/src/app/token/page.tsx @@ -0,0 +1,30 @@ +"use client"; + +import { useEffect } from "react"; + +const TokenPage = () => { + useEffect(() => { + // 1️⃣ Generate a fake token (for testing) + const token = `test_token_${Math.random().toString(36).slice(2)}`; + console.log("🔑 Generated token:", token); + + // 2️⃣ Save token to localStorage + localStorage.setItem("token", token); + console.log("💾 Token stored in localStorage"); + + // 3️⃣ Send token to window event (for extension content script) + setTimeout(() => { + console.log("📤 Posting token to window event:", token); + window.postMessage({ type: "SAVE_TO_EXTENSION", token }, "*"); + }, 500); // small delay to make sure content script is ready + }, []); + + return ( +
+

Token Page

+

Generated token is saved and sent to extension.

+
+ ); +}; + +export default TokenPage; diff --git a/browser-extension/manifest.config.ts b/browser-extension/manifest.config.ts index a81bbce..0d815ee 100644 --- a/browser-extension/manifest.config.ts +++ b/browser-extension/manifest.config.ts @@ -27,6 +27,14 @@ export default defineManifest({ js: ["src/content/main.tsx"], matches: ["https://*/*", "http://*/*"], }, + { + js: ["src/content/test.js"], + matches: ["https://*/*", "http://*/*"], + }, + { + matches: ["http://localhost:3000/*","https://untangle.rookie.house/*"], + js:["src/content.ts"] + } ], side_panel: { default_path: "src/sidepanel/index.html", diff --git a/browser-extension/package.json b/browser-extension/package.json index e5bac39..d85f8c6 100644 --- a/browser-extension/package.json +++ b/browser-extension/package.json @@ -16,7 +16,7 @@ "zod": "^4.1.8" }, "devDependencies": { - "@crxjs/vite-plugin": "^2.0.3", + "@crxjs/vite-plugin": "^2.2.0", "@types/chrome": "^0.1.11", "@types/node": "^24.3.2", "@types/react": "^19.1.8", diff --git a/browser-extension/src/content.ts b/browser-extension/src/content.ts new file mode 100644 index 0000000..fd85885 --- /dev/null +++ b/browser-extension/src/content.ts @@ -0,0 +1,23 @@ +console.log("🚀 content.ts loaded on", window.location.href); + +window.addEventListener("message", (event) => { + console.log("🟢 Content script received window message:", event); + + // if (event.origin !== "https://untangle.rookie.house") { + // console.warn("Blocked message from origin:", event.origin); + // return; + // } + + if (event.data.type === "SAVE_TO_EXTENSION") { + console.log("🟠 Forwarding token to service worker:", event.data.token); + chrome.runtime.sendMessage( + { + action: "SAVE_TOKEN", + token: event.data.token, + }, + (response) => { + console.log("🟡 Got response from service worker:", response); + } + ); + } +}); diff --git a/browser-extension/src/content/main.tsx b/browser-extension/src/content/main.tsx index 4ced6ec..74bc493 100644 --- a/browser-extension/src/content/main.tsx +++ b/browser-extension/src/content/main.tsx @@ -2,13 +2,82 @@ import { StrictMode } from "react"; import { createRoot } from "react-dom/client"; import App from "./views/App.tsx"; -console.log("[CRXJS] Hello world from content script!"); - -const container = document.createElement("div"); -container.id = "crxjs-app"; -document.body.appendChild(container); -createRoot(container).render( - - - +console.log("[UNTANGLE] Content script loaded!", window.location.href); + +// Check if we're in an iframe or restricted context +const isInIframe = window !== window.top; +const isRestrictedDomain = ['extension://', 'chrome://', 'moz-extension://'].some(protocol => + window.location.href.startsWith(protocol) ); + +console.log("[UNTANGLE] Context check:", { isInIframe, isRestrictedDomain }); + +// Wait for DOM to be ready and create the app +const initializeApp = () => { + try { + // Check if our container already exists (avoid duplicate mounting) + if (document.getElementById("crxjs-app")) { + console.log("[UNTANGLE] App already mounted, skipping..."); + return; + } + + if (!document.body) { + console.log("[UNTANGLE] Body not ready, retrying..."); + setTimeout(initializeApp, 100); + return; + } + + console.log("[UNTANGLE] Creating container..."); + const container = document.createElement("div"); + container.id = "crxjs-app"; + + // Add inline styles as fallback + container.style.cssText = ` + position: fixed !important; + right: 20px !important; + bottom: 20px !important; + z-index: 2147483647 !important; + width: auto !important; + height: auto !important; + pointer-events: auto !important; + `; + + document.body.appendChild(container); + console.log("[UNTANGLE] Container added to DOM"); + + const root = createRoot(container); + root.render( + + + + ); + + console.log("[UNTANGLE] Viewport button mounted successfully!"); + + // Verify the button is actually visible + setTimeout(() => { + const button = container.querySelector('.toggle-button'); + if (button) { + console.log("[UNTANGLE] Button found in DOM:", button); + } else { + console.error("[UNTANGLE] Button not found in DOM!"); + } + }, 1000); + + } catch (error) { + console.error("[UNTANGLE] Error initializing app:", error); + } +}; + +// Don't load in iframes or restricted domains +if (!isInIframe && !isRestrictedDomain) { + // Initialize when DOM is ready + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', initializeApp); + } else { + // DOM is already ready + initializeApp(); + } +} else { + console.log("[UNTANGLE] Skipping initialization due to context restrictions"); +} diff --git a/browser-extension/src/content/test.js b/browser-extension/src/content/test.js new file mode 100644 index 0000000..5b62d1f --- /dev/null +++ b/browser-extension/src/content/test.js @@ -0,0 +1,37 @@ +// Simple test script to verify content script injection works +console.log("[UNTANGLE TEST] Content script injection working!"); + +// Create a simple test element +const testDiv = document.createElement('div'); +testDiv.id = 'untangle-test'; +testDiv.style.cssText = ` + position: fixed; + top: 10px; + right: 10px; + background: red; + color: white; + padding: 10px; + z-index: 999999; + font-family: monospace; +`; +testDiv.textContent = 'UNTANGLE TEST'; + +// Add when DOM is ready +const addTest = () => { + if (document.body) { + document.body.appendChild(testDiv); + console.log("[UNTANGLE TEST] Test element added"); + + // Remove after 3 seconds + setTimeout(() => { + if (testDiv.parentNode) { + testDiv.parentNode.removeChild(testDiv); + console.log("[UNTANGLE TEST] Test element removed"); + } + }, 3000); + } else { + setTimeout(addTest, 100); + } +}; + +addTest(); \ No newline at end of file diff --git a/browser-extension/src/content/views/App.css b/browser-extension/src/content/views/App.css index 2a6d764..af7185f 100644 --- a/browser-extension/src/content/views/App.css +++ b/browser-extension/src/content/views/App.css @@ -60,15 +60,91 @@ body { opacity: 0; } +/* Reset any potential conflicts */ +* { + box-sizing: border-box; +} + +body { + background: transparent !important; +} + #crxjs-app { - position: fixed; - right: 0; - bottom: 0; - z-index: 9999; + position: fixed !important; + right: 20px !important; + bottom: 20px !important; + z-index: 2147483647 !important; /* Maximum z-index */ + background: transparent !important; + padding: 0 !important; + margin: 0 !important; + width: auto !important; + height: auto !important; + box-shadow: none !important; + pointer-events: auto !important; + font-family: ui-sans-serif, system-ui, sans-serif !important; + border: none !important; + outline: none !important; +} + +.toggle-button { + position: relative !important; + display: flex !important; + align-items: center !important; + justify-content: center !important; + width: 56px !important; + height: 56px !important; + border-radius: 50% !important; + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15), 0 8px 32px rgba(0, 0, 0, 0.1) !important; + cursor: pointer !important; + border: none !important; + background: linear-gradient(135deg, #288cd7, #1e6aa3) !important; + transition: all 0.3s ease !important; + overflow: hidden !important; + font-size: 24px !important; + color: white !important; + text-decoration: none !important; + font-family: inherit !important; + outline: none !important; + user-select: none !important; + -webkit-user-select: none !important; + -moz-user-select: none !important; + -ms-user-select: none !important; +} + +.toggle-button:hover { + background: linear-gradient(135deg, #1e6aa3, #155a87) !important; + transform: translateY(-2px) !important; + box-shadow: 0 6px 20px rgba(0, 0, 0, 0.2), 0 12px 40px rgba(0, 0, 0, 0.15) !important; +} + +.toggle-button:active { + transform: translateY(0) !important; +} + +.toggle-button:focus { + outline: 2px solid rgba(40, 140, 215, 0.5) !important; + outline-offset: 2px !important; +} + +/* Ensure button is always visible */ +.toggle-button::before { + content: '' !important; + position: absolute !important; + top: 0 !important; + left: 0 !important; + right: 0 !important; + bottom: 0 !important; + background: inherit !important; + border-radius: inherit !important; + z-index: -1 !important; +} + +/* Remove any potential inherited styles */ +.toggle-button * { + margin: 0 !important; + padding: 0 !important; + border: none !important; background: transparent !important; - padding: 1rem; - margin: 0.5rem; - width: auto; - height: auto; - box-shadow: none; + font-size: inherit !important; + color: inherit !important; } diff --git a/browser-extension/src/content/views/App.tsx b/browser-extension/src/content/views/App.tsx index 636535f..e718151 100644 --- a/browser-extension/src/content/views/App.tsx +++ b/browser-extension/src/content/views/App.tsx @@ -1,24 +1,59 @@ -import Logo from "@/assets/crx.svg"; import "./App.css"; +import { useState, useEffect } from "react"; function App() { + const [mounted, setMounted] = useState(false); + + useEffect(() => { + setMounted(true); + console.log("[UNTANGLE] App component mounted"); + }, []); + const openSidePanel = async () => { try { - await chrome.runtime.sendMessage({ + console.log("[UNTANGLE] Button clicked, sending message..."); + const response = await chrome.runtime.sendMessage({ action: "openSidePanelRequest", }); - console.log("Request sent to open side panel."); + console.log("[UNTANGLE] Message sent successfully, response:", response); } catch (error) { - console.error("Error sending message:", error); - alert( - "Could not send a request to open side panel. See console for details." - ); + console.error("[UNTANGLE] Error sending message:", error); + // Show user-friendly error + alert("Could not open sidepanel. Please refresh the page and try again."); } }; + if (!mounted) { + return null; + } + return ( - ); } diff --git a/browser-extension/src/service_worker.ts b/browser-extension/src/service_worker.ts index 76f0c1a..e74f617 100644 --- a/browser-extension/src/service_worker.ts +++ b/browser-extension/src/service_worker.ts @@ -1,6 +1,6 @@ // src/service_worker.ts -import api from "./lib/api/api"; +// import api from "./lib/api/api"; chrome.runtime.onMessage.addListener(async (message, sender) => { switch (message.action) { @@ -9,19 +9,19 @@ chrome.runtime.onMessage.addListener(async (message, sender) => { const { access_token } = await chrome.storage.local.get("access_token"); if (access_token) { - api.getAxios().interceptors.request.use(async (config) => { - config.headers.Authorization = `Bearer ${access_token}`; - return config; - }); + // api.getAxios().interceptors.request.use(async (config) => { + // config.headers.Authorization = `Bearer ${access_token}`; + // return config; + // }); - const userDetails = await api.auth.ping(); + // const userDetails = await api.auth.ping(); chrome.runtime.sendMessage({ action: "authStatusResponse", payload: { isAuthenticated: true, user: { - userDetails, + userDetails:"xyz", }, }, }); @@ -43,6 +43,12 @@ chrome.runtime.onMessage.addListener(async (message, sender) => { }); } break; + case "SAVE_TOKEN": + console.log("Received SAVE_TOKEN in service worker:", message.token); + chrome.storage.local.set({ access_token: message.token }, () => { + console.log("Token saved successfully in chrome.storage.local"); + }); + break; case "demistifyRequest": try { const { tabId } = message; @@ -96,6 +102,169 @@ chrome.runtime.onMessage.addListener(async (message, sender) => { } break; + case "chatRequest": + try { + const { text } = message.payload; + console.log("Processing chat request:", text); + + // Simulate processing delay + setTimeout(() => { + const demoResponses = [ + `🤖 **AI Analysis:** Your message "${text}" has been processed. Here are some insights:\n\n• Detected sentiment: Positive\n• Key topics identified: 2\n• Relevance score: 85%\n\n💡 **Suggestion:** Consider expanding on the main points mentioned.`, + + `📊 **Smart Response:** Based on your input "${text}", I've analyzed the content:\n\n• Context understanding: High\n• Action items detected: 1\n• Priority level: Medium\n\n🎯 **Next Steps:** Would you like me to elaborate on any specific aspect?`, + + `🔍 **Deep Dive:** Your query "${text}" reveals interesting patterns:\n\n• Complexity level: Moderate\n• Related concepts found: 3\n• Confidence score: 92%\n\n✨ **Insight:** This topic connects well with recent trends in the field.` + ]; + + const randomResponse = demoResponses[Math.floor(Math.random() * demoResponses.length)]; + + chrome.runtime.sendMessage({ + action: "chatResponse", + payload: { text: randomResponse } + }); + }, 1000); + + } catch (error) { + console.error("Error processing chat request:", error); + chrome.runtime.sendMessage({ + action: "chatResponse", + payload: { text: "Sorry, I encountered an error processing your request." } + }); + } + break; + + case "takeScreenshot": + try { + const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true }); + if (activeTab && activeTab.id) { + const dataUrl = await chrome.tabs.captureVisibleTab(activeTab.windowId, { format: "png" }); + + // Simulate screenshot analysis + setTimeout(() => { + const screenshotAnalysis = `📸 **Screenshot Analysis Complete:**\n\n🔍 **Visual Elements Detected:**\n• UI components: 12\n• Text blocks: 8\n• Interactive elements: 5\n• Images/graphics: 3\n\n📋 **Page Insights:**\n• Layout quality: Good\n• Accessibility score: 78%\n• Mobile responsiveness: Detected\n\n💡 **Recommendations:**\n• Consider improving contrast in header area\n• Some buttons could be larger for better UX`; + + chrome.runtime.sendMessage({ + action: "screenshotResponse", + payload: { + text: screenshotAnalysis, + imageData: dataUrl + } + }); + }, 1500); + } + } catch (error) { + console.error("Error taking screenshot:", error); + } + break; + + case "highlightRandomText": + try { + const { tabId } = await chrome.tabs.query({ active: true, currentWindow: true }).then(tabs => ({ tabId: tabs[0]?.id })); + + if (tabId) { + const highlightFunction = () => { + // Remove any existing highlights + const existingHighlights = document.querySelectorAll('.untangle-highlight'); + existingHighlights.forEach(el => { + const parent = el.parentNode; + if (parent) { + parent.replaceChild(document.createTextNode(el.textContent || ''), el); + parent.normalize(); + } + }); + + // Find text nodes and highlight random ones + const walker = document.createTreeWalker( + document.body, + NodeFilter.SHOW_TEXT, + { + acceptNode: (node) => { + const text = node.textContent?.trim() || ''; + return text.length > 20 && + !node.parentElement?.closest('script, style, noscript, iframe') && + text.split(' ').length > 3 + ? NodeFilter.FILTER_ACCEPT + : NodeFilter.FILTER_REJECT; + } + } + ); + + const textNodes: Text[] = []; + let node; + while (node = walker.nextNode()) { + textNodes.push(node as Text); + } + + // Randomly select 3-5 text nodes to highlight + const numHighlights = Math.min(Math.floor(Math.random() * 3) + 3, textNodes.length); + const selectedNodes = textNodes + .sort(() => 0.5 - Math.random()) + .slice(0, numHighlights); + + selectedNodes.forEach(textNode => { + const text = textNode.textContent || ''; + const words = text.split(' '); + + if (words.length > 5) { + // Highlight a random phrase of 2-4 words + const startIndex = Math.floor(Math.random() * (words.length - 4)); + const phraseLength = Math.floor(Math.random() * 3) + 2; + const phrase = words.slice(startIndex, startIndex + phraseLength).join(' '); + + const beforeText = words.slice(0, startIndex).join(' '); + const afterText = words.slice(startIndex + phraseLength).join(' '); + + const highlight = document.createElement('span'); + highlight.className = 'untangle-highlight'; + highlight.style.cssText = ` + background: linear-gradient(120deg, rgba(255, 255, 0, 0.3) 0%, rgba(255, 165, 0, 0.3) 100%); + padding: 2px 4px; + border-radius: 3px; + box-shadow: 0 1px 3px rgba(0,0,0,0.1); + font-weight: 500; + animation: highlight-pulse 2s ease-in-out; + `; + highlight.textContent = phrase; + + const fragment = document.createDocumentFragment(); + if (beforeText) fragment.appendChild(document.createTextNode(beforeText + ' ')); + fragment.appendChild(highlight); + if (afterText) fragment.appendChild(document.createTextNode(' ' + afterText)); + + textNode.parentNode?.replaceChild(fragment, textNode); + } + }); + + // Add CSS animation if not exists + if (!document.getElementById('untangle-highlight-styles')) { + const style = document.createElement('style'); + style.id = 'untangle-highlight-styles'; + style.textContent = ` + @keyframes highlight-pulse { + 0% { background: rgba(255, 255, 0, 0.6); transform: scale(1.02); } + 50% { background: rgba(255, 165, 0, 0.4); } + 100% { background: rgba(255, 255, 0, 0.3); transform: scale(1); } + } + `; + document.head.appendChild(style); + } + + return `Highlighted ${numHighlights} text sections on the page`; + }; + + const [result] = await chrome.scripting.executeScript({ + target: { tabId: tabId }, + func: highlightFunction, + }); + + console.log('Highlighting result:', result?.result); + } + } catch (error) { + console.error('Error highlighting text:', error); + } + break; + case "openSidePanelRequest": diff --git a/browser-extension/src/sidepanel/App.css b/browser-extension/src/sidepanel/App.css index d8789e9..168a0eb 100644 --- a/browser-extension/src/sidepanel/App.css +++ b/browser-extension/src/sidepanel/App.css @@ -1,6 +1,6 @@ /* * Modern Dark Chat Interface CSS - * Replace your existing CSS with this + * Clean and properly structured styles */ body { @@ -50,6 +50,37 @@ body { text-align: center; } +/* Action button styles for auth */ +.action-btn { + padding: 12px 24px; + border: none; + border-radius: 12px; + font-size: 16px; + font-weight: 600; + cursor: pointer; + transition: all 0.2s ease; + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + backdrop-filter: blur(10px); +} + +.primary-btn { + background: linear-gradient(135deg, #3b82f6, #8b5cf6); + color: white; + box-shadow: 0 4px 16px rgba(59, 130, 246, 0.3); +} + +.primary-btn:hover { + transform: translateY(-1px); + box-shadow: 0 6px 24px rgba(59, 130, 246, 0.4); +} + +.primary-btn:active { + transform: translateY(0); +} + .chat-messages { flex-grow: 1; overflow-y: auto; @@ -136,18 +167,6 @@ body { bottom: 0; } -/* Input wrapper to contain input and screenshot button */ -.input-wrapper { - flex-grow: 1; - position: relative; - padding: 20px 24px; - background: rgba(15, 23, 42, 0.8); - backdrop-filter: blur(20px); - border-top: 1px solid rgba(148, 163, 184, 0.1); - position: sticky; - bottom: 0; -} - /* Input wrapper to contain input and screenshot button */ .input-wrapper { flex-grow: 1; @@ -159,23 +178,7 @@ body { /* Style for the text input field */ .chat-input { flex-grow: 1; - padding: 16px 50px 16px 20px; /* Right padding for screenshot button */ - background: rgba(30, 41, 59, 0.8); - border: 1px solid rgba(148, 163, 184, 0.2); - border-radius: 16px; - color: #e2e8f0; - font-size: 16px; - outline: none; - transition: all 0.2s ease; - backdrop-filter: blur(10px); - box-sizing: border-box; - height: 52px; -} - -/* Style for the text input field */ -.chat-input { - flex-grow: 1; - padding: 16px 50px 16px 20px; /* Right padding for screenshot button */ + padding: 16px 100px 16px 20px; /* Right padding for buttons */ background: rgba(30, 41, 59, 0.8); border: 1px solid rgba(148, 163, 184, 0.2); border-radius: 16px; @@ -193,30 +196,39 @@ body { box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); } -.chat-input::placeholder { - color: #64748b; -.chat-input:focus { - border-color: #3b82f6; - box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); -} - .chat-input::placeholder { color: #64748b; } -/* Screenshot button inside input */ -.screenshot-btn { +/* File upload button inside input */ +.file-upload-btn { position: absolute; - right: 8px; + right: 52px; top: 50%; transform: translateY(-50%); width: 36px; height: 36px; - background: rgba(59, 130, 246, 0.1); - border: 1px solid rgba(59, 130, 246, 0.2); + background: rgba(34, 197, 94, 0.1); + border: 1px solid rgba(34, 197, 94, 0.2); border-radius: 8px; - color: #60a5fa; + color: #4ade80; cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.2s ease; + backdrop-filter: blur(10px); + z-index: 10; + font-size: 16px; +} + +.file-upload-btn:hover { + background: rgba(34, 197, 94, 0.2); + border-color: rgba(34, 197, 94, 0.4); + color: #22c55e; + transform: translateY(-50%) scale(1.05); +} + /* Screenshot button inside input */ .screenshot-btn { position: absolute; @@ -295,72 +307,6 @@ body { border-radius: 50%; } -/* Style for the send button */ -.chat-send-btn { - width: 52px; - height: 52px; - transition: all 0.2s ease; - backdrop-filter: blur(10px); - z-index: 10; -} - -.screenshot-btn:hover { - background: rgba(59, 130, 246, 0.2); - border-color: rgba(59, 130, 246, 0.4); - color: #93c5fd; - transform: translateY(-50%) scale(1.05); -} - -.screenshot-btn:active { - transform: translateY(-50%) scale(0.95); -} - -/* Better screenshot icon using CSS */ -.screenshot-icon { - width: 16px; - height: 16px; - position: relative; -} - -.screenshot-icon::before { - content: '📷'; - font-size: 16px; - display: block; -} - -/* Alternative CSS-only camera icon */ -.screenshot-icon-alt { - width: 16px; - height: 12px; - border: 2px solid currentColor; - border-radius: 2px; - position: relative; -} - -.screenshot-icon-alt::before { - content: ''; - position: absolute; - top: -4px; - left: 50%; - transform: translateX(-50%); - width: 8px; - height: 3px; - background: currentColor; - border-radius: 2px 2px 0 0; -} - -.screenshot-icon-alt::after { - content: ''; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - width: 6px; - height: 6px; - border: 1.5px solid currentColor; - border-radius: 50%; -} - /* Style for the send button */ .chat-send-btn { width: 52px; @@ -371,36 +317,19 @@ body { color: white; font-size: 16px; font-weight: 600; - border-radius: 16px; - background: linear-gradient(135deg, #3b82f6, #8b5cf6); - color: white; - font-size: 16px; - font-weight: 600; cursor: pointer; transition: all 0.2s ease; display: flex; align-items: center; justify-content: center; box-shadow: 0 4px 16px rgba(59, 130, 246, 0.3); - display: flex; - align-items: center; - justify-content: center; - box-shadow: 0 4px 16px rgba(59, 130, 246, 0.3); } -.chat-send-btn:hover { .chat-send-btn:hover { transform: translateY(-1px); box-shadow: 0 6px 24px rgba(59, 130, 246, 0.4); - box-shadow: 0 6px 24px rgba(59, 130, 246, 0.4); -} - -.chat-send-btn:active { - transform: translateY(0); } -.chat-send-btn:disabled { - opacity: 0.4; .chat-send-btn:active { transform: translateY(0); } @@ -411,20 +340,6 @@ body { transform: none; } -/* Add some cool animations */ -/* Add some cool animations */ -@keyframes slideIn { - from { - opacity: 0; - transform: translateY(20px); - transform: translateY(20px); - } - to { - opacity: 1; - transform: translateY(0); - } -} - /* Loading animation - pure dots without text */ .loading-message { background: rgba(30, 41, 59, 0.9); @@ -482,17 +397,6 @@ body { animation-delay: 0s; } -@keyframes bounce { - 0%, 80%, 100% { - transform: scale(0.8); - opacity: 0.5; - } - 40% { - transform: scale(1.2); - opacity: 1; - } -} - /* Screenshot display styles */ .screenshot-container { display: flex; @@ -510,61 +414,21 @@ body { } .screenshot-container p { -/* Loading animation - pure dots without text */ -.loading-message { - background: rgba(30, 41, 59, 0.9); - color: #e2e8f0; - align-self: flex-start; - border-bottom-left-radius: 6px; - border-left: 3px solid #3b82f6; - position: relative; - max-width: 80px; - padding: 20px 24px; - border-radius: 20px; - backdrop-filter: blur(10px); - border: 1px solid rgba(148, 163, 184, 0.1); - animation: slideIn 0.3s ease-out; - box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2); -} - -.loading-message::before { - content: ''; - position: absolute; - left: -12px; - top: 50%; - transform: translateY(-50%); - width: 8px; - height: 8px; - background: #3b82f6; - border-radius: 50%; - box-shadow: 0 0 12px rgba(59, 130, 246, 0.6); -} - -.loading-dots { - display: flex; - gap: 6px; - align-items: center; - justify-content: center; -} - -.loading-dots .dot { - width: 8px; - height: 8px; - background: #60a5fa; - border-radius: 50%; - animation: bounce 1.4s ease-in-out infinite both; -} - -.loading-dots .dot:nth-child(1) { - animation-delay: -0.32s; -} - -.loading-dots .dot:nth-child(2) { - animation-delay: -0.16s; + margin: 0; + font-size: 14px; + opacity: 0.8; } -.loading-dots .dot:nth-child(3) { - animation-delay: 0s; +/* Animations */ +@keyframes slideIn { + from { + opacity: 0; + transform: translateY(20px); + } + to { + opacity: 1; + transform: translateY(0); + } } @keyframes bounce { @@ -578,57 +442,6 @@ body { } } -/* Screenshot display styles */ -.screenshot-container { - display: flex; - flex-direction: column; - gap: 8px; -} - -.screenshot-image { - max-width: 100%; - max-height: 200px; - object-fit: contain; - border-radius: 8px; - border: 1px solid rgba(148, 163, 184, 0.2); - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3); -} - -.screenshot-container p { - margin: 0; - font-size: 14px; - opacity: 0.8; - font-size: 14px; - opacity: 0.8; -} - -/* Add a subtle pulse animation for AI messages */ -.demistify-message:first-child { - animation: slideIn 0.3s ease-out, pulse 2s infinite; -} - -@keyframes pulse { - 0% { - box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2); - } - 50% { - box-shadow: 0 4px 20px rgba(59, 130, 246, 0.3); - } - 100% { - box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2); - } -} - -/* Mobile responsiveness */ -@media (max-width: 640px) { - .chat-messages { - padding: 16px; - gap: 12px; -/* Add a subtle pulse animation for AI messages */ -.demistify-message:first-child { - animation: slideIn 0.3s ease-out, pulse 2s infinite; -} - @keyframes pulse { 0% { box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2); @@ -648,19 +461,14 @@ body { gap: 12px; } - .chat-message { - max-width: 90%; - padding: 12px 16px; .chat-message { max-width: 90%; padding: 12px 16px; } - .chat-input-row { .chat-input-row { padding: 16px; gap: 8px; - gap: 8px; } .chat-input { @@ -687,28 +495,4 @@ body { radial-gradient(circle at 80% 20%, rgba(139, 92, 246, 0.1) 0%, transparent 50%); pointer-events: none; z-index: -1; - .chat-input { - font-size: 16px; /* Prevents zoom on iOS */ - padding: 12px 16px; - } - - .chat-send-btn { - width: 44px; - height: 44px; - font-size: 14px; - } } - -/* Add a subtle glow effect to the entire panel */ -.chat-panel::before { - content: ''; - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: radial-gradient(circle at 20% 50%, rgba(59, 130, 246, 0.1) 0%, transparent 50%), - radial-gradient(circle at 80% 20%, rgba(139, 92, 246, 0.1) 0%, transparent 50%); - pointer-events: none; - z-index: -1; -} \ No newline at end of file diff --git a/browser-extension/src/sidepanel/App.tsx b/browser-extension/src/sidepanel/App.tsx index 3c34bc6..c1f2d3f 100644 --- a/browser-extension/src/sidepanel/App.tsx +++ b/browser-extension/src/sidepanel/App.tsx @@ -64,6 +64,26 @@ export default function App() { { type: "llm", text: message.payload.text }, ]; }); + } else if (message.action === "chatResponse" && message.payload) { + setMessages((prevMessages) => { + const filteredMessages = prevMessages.filter( + (msg) => msg.type !== "loading" + ); + return [ + ...filteredMessages, + { type: "llm", text: message.payload.text }, + ]; + }); + } else if (message.action === "screenshotResponse" && message.payload) { + setMessages((prevMessages) => { + const filteredMessages = prevMessages.filter( + (msg) => msg.type !== "loading" + ); + return [ + ...filteredMessages, + { type: "llm", text: message.payload.text }, + ]; + }); } }; @@ -84,6 +104,59 @@ export default function App() { }); }; + const handleFileUpload = (event: React.ChangeEvent) => { + const file = event.target.files?.[0]; + if (file) { + const reader = new FileReader(); + reader.onload = () => { + // Add user message showing file upload + setMessages((prev) => [ + ...prev, + { type: "user", text: `📁 Uploaded file: ${file.name}` }, + { type: "loading", text: "" }, + ]); + + // Simulate processing and generate demo response + setTimeout(() => { + const demoResponse = generateDemoResponse(file.name); + setMessages((prev) => { + const filteredMessages = prev.filter(msg => msg.type !== "loading"); + return [ + ...filteredMessages, + { type: "llm", text: demoResponse } + ]; + }); + + // Trigger random highlighting on the page + chrome.runtime.sendMessage({ + action: "highlightRandomText", + payload: { fileName: file.name } + }); + }, 1500); + }; + + if (file.type.startsWith('text/')) { + reader.readAsText(file); + } else { + reader.readAsDataURL(file); + } + } + }; + + const generateDemoResponse = (fileName: string): string => { + const responses = [ + `🔍 **Analysis of ${fileName}:**\n\n✨ **Key Insights:**\n• This document contains valuable information about project requirements\n• Found 3 important action items that need attention\n• Detected potential compliance issues in sections 2-4\n• Recommended next steps: Review with legal team\n\n📊 **Summary:** This file appears to be a critical business document with high priority items.`, + + `📋 **File Summary for ${fileName}:**\n\n🎯 **Important Findings:**\n• Document contains financial data requiring secure handling\n• 5 key performance indicators identified\n• Risk assessment shows medium-high priority\n• Stakeholder approval needed for next phase\n\n💡 **AI Recommendation:** Schedule review meeting within 48 hours.`, + + `🔎 **Document Intelligence Results:**\n\n🚀 **Key Highlights:**\n• Technical specifications meet current standards\n• Implementation timeline: 2-3 weeks estimated\n• Resource allocation appears optimal\n• Quality assurance checkpoints established\n\n⚡ **Action Required:** Begin phase 1 implementation immediately.`, + + `📄 **Content Analysis Complete:**\n\n🎪 **Notable Elements:**\n• Strategic planning document with Q4 objectives\n• Budget allocation requires CFO approval\n• Cross-department collaboration needed\n• Success metrics clearly defined\n\n🔔 **Priority Level:** High - Requires executive review.` + ]; + + return responses[Math.floor(Math.random() * responses.length)]; + }; + const handleSend = () => { if (input.trim()) { setMessages((prev) => [ @@ -165,8 +238,22 @@ export default function App() { onChange={(e) => setInput(e.target.value)} onKeyPress={handleKeyPress} className="chat-input" - placeholder="Type a message or take a screenshot..." + placeholder="Type a message or upload a file..." + /> + +