SurfSense/surfsense_browser_extension/content.ts

143 lines
3.2 KiB
TypeScript
Raw Normal View History

2025-07-27 12:01:11 -07:00
import type { PlasmoCSConfig } from "plasmo";
2025-04-07 23:47:06 -07:00
2025-07-27 12:01:11 -07:00
export const config: PlasmoCSConfig = {
matches: ["<all_urls>"],
all_frames: true,
world: "MAIN",
};
/**
* Content script for page context detection
* Extracts relevant data from crypto pages and sends to side panel
*/
type PageType = "dexscreener" | "coingecko" | "twitter" | "generic";
interface TokenData {
chain: string;
pairAddress: string;
tokenSymbol?: string;
price?: string;
volume24h?: string;
liquidity?: string;
}
interface PageContext {
url: string;
title: string;
pageType: PageType;
tokenData?: TokenData;
}
/**
* Detect page type from URL
*/
function detectPageType(url: string): PageType {
if (url.includes("dexscreener.com")) return "dexscreener";
if (url.includes("coingecko.com")) return "coingecko";
if (url.includes("twitter.com") || url.includes("x.com")) return "twitter";
return "generic";
}
/**
* Extract DexScreener token data from DOM
*/
function extractDexScreenerData(): TokenData | undefined {
const url = window.location.href;
const match = url.match(/dexscreener\.com\/([^\/]+)\/([^\/\?]+)/);
if (!match) return undefined;
const [, chain, pairAddress] = match;
// Try to extract data from DOM
// Note: DexScreener uses dynamic rendering, so selectors may need adjustment
const tokenSymbol =
document.querySelector('[data-test="token-symbol"]')?.textContent ||
document.querySelector(".token-symbol")?.textContent ||
undefined;
const price =
document.querySelector('[data-test="token-price"]')?.textContent ||
document.querySelector(".token-price")?.textContent ||
undefined;
const volume24h =
document.querySelector('[data-test="volume-24h"]')?.textContent ||
document.querySelector(".volume-24h")?.textContent ||
undefined;
const liquidity =
document.querySelector('[data-test="liquidity"]')?.textContent ||
document.querySelector(".liquidity")?.textContent ||
undefined;
return {
chain,
pairAddress,
tokenSymbol,
price,
volume24h,
liquidity,
};
}
/**
* Extract page context based on page type
*/
function extractPageContext(): PageContext {
const url = window.location.href;
const title = document.title;
const pageType = detectPageType(url);
const context: PageContext = {
url,
title,
pageType,
};
// Add page-specific data
if (pageType === "dexscreener") {
context.tokenData = extractDexScreenerData();
}
return context;
}
/**
* Send context update to side panel
*/
function sendContextUpdate() {
const context = extractPageContext();
chrome.runtime.sendMessage({
type: "PAGE_CONTEXT_UPDATE",
data: context,
});
}
// Send initial context after page load
if (document.readyState === "complete") {
sendContextUpdate();
} else {
window.addEventListener("load", sendContextUpdate);
}
// Listen for context requests from side panel
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.type === "GET_PAGE_CONTEXT") {
sendContextUpdate();
}
});
// Watch for DOM changes (for dynamic content like DexScreener)
const observer = new MutationObserver(() => {
// Debounce updates
clearTimeout((window as any).__contextUpdateTimeout);
(window as any).__contextUpdateTimeout = setTimeout(sendContextUpdate, 1000);
});
observer.observe(document.body, {
childList: true,
subtree: true,
});