Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions apps/web/src/app/token/page.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<div className="min-h-screen flex flex-col items-center justify-center bg-gray-900 text-white">
<h1 className="text-3xl font-bold mb-2">Token Page</h1>
<p className="text-gray-400">Generated token is saved and sent to extension.</p>
</div>
);
};

export default TokenPage;
8 changes: 8 additions & 0 deletions browser-extension/manifest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion browser-extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
23 changes: 23 additions & 0 deletions browser-extension/src/content.ts
Original file line number Diff line number Diff line change
@@ -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);
}
);
}
});
87 changes: 78 additions & 9 deletions browser-extension/src/content/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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(
<StrictMode>
<App />
</StrictMode>
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)
);
Comment on lines +9 to 11
Copy link

Copilot AI Sep 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The protocol check could be more robust by using window.location.protocol instead of checking the full href. This would be more precise: ['extension:', 'chrome:', 'moz-extension:'].includes(window.location.protocol)

Suggested change
const isRestrictedDomain = ['extension://', 'chrome://', 'moz-extension://'].some(protocol =>
window.location.href.startsWith(protocol)
);
const isRestrictedDomain = ['extension:', 'chrome:', 'moz-extension:'].includes(window.location.protocol);

Copilot uses AI. Check for mistakes.

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(
<StrictMode>
<App />
</StrictMode>
);

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");
}
37 changes: 37 additions & 0 deletions browser-extension/src/content/test.js
Original file line number Diff line number Diff line change
@@ -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();
94 changes: 85 additions & 9 deletions browser-extension/src/content/views/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
53 changes: 44 additions & 9 deletions browser-extension/src/content/views/App.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<button className="toggle-button" onClick={openSidePanel}>
<img src={Logo} alt="CRXJS logo" className="button-icon" />
<button
className="toggle-button"
onClick={openSidePanel}
title="Open Untangle Sidepanel"
aria-label="Open Untangle Sidepanel"
style={{
// Inline styles as fallback
position: 'fixed',
right: '20px',
bottom: '20px',
zIndex: 2147483647,
width: '56px',
height: '56px',
borderRadius: '50%',
border: 'none',
background: 'linear-gradient(135deg, #288cd7, #1e6aa3)',
color: 'white',
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: '24px',
boxShadow: '0 4px 16px rgba(0, 0, 0, 0.15)',
}}
>
📋
</button>
);
}
Expand Down
Loading