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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
# misc
.DS_Store
*.pem
*.bak

# debug
npm-debug.log*
Expand Down
Binary file added assets/chrome.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 6 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@
"react": "18.2.0",
"react-dom": "18.2.0",
"tailwindcss": "3.4.1",
"util": "^0.12.5",
"uuid": "^13.0.0"
"util": "^0.12.5"
},
"devDependencies": {
"@babel/preset-env": "^7.26.9",
Expand Down Expand Up @@ -63,6 +62,10 @@
},
"manifest": {
"permissions": [
"tabs",
"history",
"activeTab",
"scripting",
"storage",
"cookies"
],
Expand All @@ -72,4 +75,4 @@
]
},
"type": "module"
}
}
167 changes: 167 additions & 0 deletions src/background.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,170 @@
// This is used to get all tabs in the browser, and some of their conten
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
// Handle getTabs request for Chrome Tabs connection
if (request.action === "getTabs") {
chrome.tabs.query({}, (tabs) => {
if (chrome.runtime.lastError) {
sendResponse({ error: chrome.runtime.lastError.message });
} else {
sendResponse({ tabs: tabs });
}
});
return true;
}

// Handle getTabsWithContent request
if (request.action === "getTabsWithContent") {
chrome.tabs.query({}, async (tabs) => {
if (chrome.runtime.lastError) {
sendResponse({ error: chrome.runtime.lastError.message });
return;
}

const tabsWithContent = [];

for (const tab of tabs) {
const tabData = { ...tab, pageContent: '' }; // Add pageContent property

// Try to get page content for each tab
try {
if (tab.id && tab.url && !tab.url.startsWith('chrome://') && !tab.url.startsWith('chrome-extension://')) {
// Execute content script to get page text
const results = await chrome.scripting.executeScript({
target: { tabId: tab.id },
func: getPageContent, // Use 'func' instead of 'function'
});

if (results && results[0] && results[0].result) {
tabData.pageContent = results[0].result;
}
}
} catch (error) {
console.error(`Could not get content for tab ${tab.id}:`, error);
// Set a fallback description
tabData.pageContent = `Content from ${tab.url ? new URL(tab.url).hostname : 'unknown site'} - unable to read page content`;
}

tabsWithContent.push(tabData);
}

sendResponse({ tabs: tabsWithContent });
});
return true;
}

// Don't interfere with other message handlers
return false;
});

// This gets the page content from a tab.
function getPageContent() {
try {
const title = document.title || '';
const url = window.location.href;
const domain = window.location.hostname;

// Get ALL visible text from the page
let allText = '';

// Method 1: Try to get all text from body
if (document.body) {
// Get all text content, which automatically excludes HTML tags
allText = document.body.innerText || document.body.textContent || '';
}

// If body approach fails, try document-wide text extraction
if (!allText || allText.length < 100) {
// Get all text nodes in the document
const walker = document.createTreeWalker(
document.body || document.documentElement,
NodeFilter.SHOW_TEXT,
{
acceptNode: function(node) {
// Skip script, style, and other non-visible content
const parent = node.parentElement;
if (!parent) return NodeFilter.FILTER_REJECT;

const tagName = parent.tagName.toLowerCase();
if (['script', 'style', 'noscript', 'iframe', 'object'].includes(tagName)) {
return NodeFilter.FILTER_REJECT;
}

// Skip if parent is hidden
const style = window.getComputedStyle(parent);
if (style.display === 'none' || style.visibility === 'hidden') {
return NodeFilter.FILTER_REJECT;
}

// Only accept text nodes with meaningful content
const text = node.textContent?.trim() || '';
if (text.length < 3) return NodeFilter.FILTER_REJECT;

return NodeFilter.FILTER_ACCEPT;
}
}
);

const textNodes = [];
let node;
while (node = walker.nextNode()) {
const text = node.textContent?.trim();
if (text && text.length > 2) {
textNodes.push(text);
}
}

allText = textNodes.join(' ');
}

// Clean up the text
allText = allText
.replace(/\s+/g, ' ') // Replace multiple whitespace with single space
.replace(/\n+/g, ' ') // Replace newlines with spaces
.replace(/\t+/g, ' ') // Replace tabs with spaces
.trim();

// Take a reasonable sample of the text (first 300 chars)
const textSample = allText.substring(0, 300);

// Combine title and text content
let result = '';
if (title && title.trim()) {
result += `${title.trim()}. `;
}

if (textSample && textSample.length > 10) {
// Remove title from content if it's repeated
let contentText = textSample;
if (title && textSample.toLowerCase().startsWith(title.toLowerCase())) {
contentText = textSample.substring(title.length).trim();
if (contentText.startsWith('.') || contentText.startsWith('-')) {
contentText = contentText.substring(1).trim();
}
}

if (contentText.length > 10) {
result += contentText;
}
}

// Generic fallback if no meaningful content found
if (!result.trim() || result.trim().length < 20) {
result = `Content from ${domain} - ${title || url.split('/').pop() || 'webpage'}`;
}

return result || `Page from ${domain}`;

} catch (error) {
console.error('Error extracting page content:', error);

// Simple fallback
const domain = window.location.hostname;
const title = document.title || '';

return title || `Content from ${domain}`;
}
}

// This is used to register cookies in the browser
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.action === "setCookie") {
Expand Down
3 changes: 2 additions & 1 deletion src/connection_manager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import { GoogleScholarConnection } from "./connections/googleScholar/connection"
import { WikipediaSegmentConnection } from "./connections/wikipediaSegment/connection";
import { GmailConnection } from "./connections/Gmail/connection";
import { LinkedInConnection } from "./connections/Linkedin/connection";
import { ChromeTabsConnection } from "./connections/chromeTabs/connection";


export const CONNECTIONS = [GmailConnection, WikipediaSegmentConnection, WikipediaReferencesConnection, GoogleConnection, PubmedConnection, GoogleDocsConnection, GoogleScholarConnection,LinkedInConnection];
export const CONNECTIONS = [GmailConnection, WikipediaSegmentConnection, WikipediaReferencesConnection, GoogleConnection, PubmedConnection, GoogleDocsConnection, GoogleScholarConnection,LinkedInConnection, ChromeTabsConnection];

export const searchConnections = (url: string, ) => {
const connections = CONNECTIONS.filter(connection => connection.trigger(url));
Expand Down
20 changes: 10 additions & 10 deletions src/connections/Linkedin/connection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { GenerationProgress } from "../types";
import { getSpacePortal, registerAuthCookies, reqSpaceCreation } from "../../driver";
import wikiIcon from "data-base64:../../../assets/wiki.png";

import { v4 as uuidv4 } from 'uuid';
import { getUuidV4 } from "../../driver";



Expand Down Expand Up @@ -65,7 +65,7 @@ const createSpace = async (
const company = row[companyIdx];
const url = linkIdx !== -1 ? row[linkIdx] : "";
result.push({
uuid: uuidv4(),
uuid: getUuidV4(),
title: `Applied Job: ${title}`,
text: `Applied to ${title} at ${company}`,
link: url,
Expand Down Expand Up @@ -122,7 +122,7 @@ const createSpace = async (
const name = document.querySelector("h1.text-heading-xlarge")?.textContent?.trim() || "Unknown Name";
const headline = document.querySelector(".text-body-medium.break-words")?.textContent?.trim() || "";
extractedData.push({
uuid: uuidv4(),
uuid: getUuidV4(),
title: name,
text: sanitize(headline),
link: window.location.href,
Expand All @@ -139,7 +139,7 @@ const createSpace = async (
const about = aboutSection?.innerText?.trim();
if (about) {
extractedData.push({
uuid: uuidv4(),
uuid: getUuidV4(),
title: "About",
text: sanitize(about),
link: window.location.href,
Expand All @@ -161,7 +161,7 @@ const createSpace = async (
const description = entry.innerText?.trim();
if (jobTitle && description) {
extractedData.push({
uuid: uuidv4(),
uuid: getUuidV4(),
title: `Experience: ${jobTitle}`,
text: sanitize(description),
link: window.location.href,
Expand All @@ -184,7 +184,7 @@ const createSpace = async (
const eduDetails = entry.innerText?.trim();
if (school && eduDetails) {
extractedData.push({
uuid: uuidv4(),
uuid: getUuidV4(),
title: `Education: ${school}`,
text: sanitize(eduDetails),
link: window.location.href,
Expand All @@ -206,7 +206,7 @@ const createSpace = async (
if (!seen.has(connectionUrl)) {
seen.add(connectionUrl);
extractedData.push({
uuid: uuidv4(),
uuid: getUuidV4(),
title: `Connection: ${connectionName}`,
text: `Connected with ${connectionName}`,
link: connectionUrl,
Expand All @@ -232,7 +232,7 @@ if (activitySection) {
const postContent = card.textContent?.trim().replace(/\s+/g, " ") || "LinkedIn Activity";

extractedData.push({
uuid: uuidv4(),
uuid: getUuidV4(),
title: `Activity: ${postContent.slice(0, 40)}...`,
text: postContent,
link: postUrl,
Expand Down Expand Up @@ -269,7 +269,7 @@ const getMessagesFromIframe = async (): Promise<any[]> => {
: "https://www.linkedin.com/messaging/";

return {
uuid: uuidv4(),
uuid: getUuidV4(),
title: `Message with ${name}`,
text: `${timestamp} - ${snippet}`,
link: threadUrl,
Expand Down Expand Up @@ -312,7 +312,7 @@ const getFollowedCompanies = async (): Promise<any[]> => {
const link = (card.querySelector("a") as HTMLAnchorElement)?.href || "";

return {
uuid: uuidv4(),
uuid: getUuidV4(),
title: `Following: ${name}`,
text: subtitle,
link,
Expand Down
Loading