Biome: Fixes for extenstion repo

This commit is contained in:
Utkarsh-Patel-13 2025-07-27 12:01:11 -07:00
parent b70d46f732
commit b76419c6fc
29 changed files with 2668 additions and 2386 deletions

View file

@ -1,26 +0,0 @@
/**
* @type {import('prettier').Options}
*/
export default {
printWidth: 80,
tabWidth: 2,
useTabs: false,
semi: false,
singleQuote: false,
trailingComma: "none",
bracketSpacing: true,
bracketSameLine: true,
plugins: ["@ianvs/prettier-plugin-sort-imports"],
importOrder: [
"<BUILTIN_MODULES>", // Node.js built-in modules
"<THIRD_PARTY_MODULES>", // Imports not matched by other special words or groups.
"", // Empty line
"^@plasmo/(.*)$",
"",
"^@plasmohq/(.*)$",
"",
"^~(.*)$",
"",
"^[./]"
]
}

View file

@ -1,77 +1,70 @@
import { initQueues, initWebHistory } from "~utils/commons"
import type { WebHistory } from "~utils/interfaces"
import { Storage } from "@plasmohq/storage"
import {getRenderedHtml} from '~utils/commons'
import { Storage } from "@plasmohq/storage";
import { getRenderedHtml, initQueues, initWebHistory } from "~utils/commons";
import type { WebHistory } from "~utils/interfaces";
chrome.tabs.onCreated.addListener(async (tab: any) => {
try {
await initWebHistory(tab.id)
await initQueues(tab.id)
await initWebHistory(tab.id);
await initQueues(tab.id);
} catch (error) {
console.log(error)
console.log(error);
}
})
});
chrome.tabs.onUpdated.addListener(
async (tabId: number, changeInfo: any, tab: any) => {
chrome.tabs.onUpdated.addListener(async (tabId: number, changeInfo: any, tab: any) => {
if (changeInfo.status === "complete" && tab.url) {
const storage = new Storage({ area: "local" })
await initWebHistory(tab.id)
await initQueues(tab.id)
const storage = new Storage({ area: "local" });
await initWebHistory(tab.id);
await initQueues(tab.id);
const result = await chrome.scripting.executeScript({
// @ts-ignore
target: { tabId: tab.id },
// @ts-ignore
func: getRenderedHtml
})
func: getRenderedHtml,
});
let toPushInTabHistory: any = result[0].result // const { renderedHtml, title, url, entryTime } = result[0].result;
const toPushInTabHistory: any = result[0].result; // const { renderedHtml, title, url, entryTime } = result[0].result;
let urlQueueListObj: any = await storage.get("urlQueueList")
let timeQueueListObj: any = await storage.get("timeQueueList")
const urlQueueListObj: any = await storage.get("urlQueueList");
const timeQueueListObj: any = await storage.get("timeQueueList");
urlQueueListObj.urlQueueList
.find((data: WebHistory) => data.tabsessionId === tabId)
.urlQueue.push(toPushInTabHistory.url)
.urlQueue.push(toPushInTabHistory.url);
timeQueueListObj.timeQueueList
.find((data: WebHistory) => data.tabsessionId === tabId)
.timeQueue.push(toPushInTabHistory.entryTime)
.timeQueue.push(toPushInTabHistory.entryTime);
await storage.set("urlQueueList", {
urlQueueList: urlQueueListObj.urlQueueList
})
urlQueueList: urlQueueListObj.urlQueueList,
});
await storage.set("timeQueueList", {
timeQueueList: timeQueueListObj.timeQueueList
})
timeQueueList: timeQueueListObj.timeQueueList,
});
}
}
)
});
chrome.tabs.onRemoved.addListener(async (tabId: number, removeInfo: object) => {
const storage = new Storage({ area: "local" })
let urlQueueListObj: any = await storage.get("urlQueueList")
let timeQueueListObj: any = await storage.get("timeQueueList")
const storage = new Storage({ area: "local" });
const urlQueueListObj: any = await storage.get("urlQueueList");
const timeQueueListObj: any = await storage.get("timeQueueList");
if (urlQueueListObj.urlQueueList && timeQueueListObj.timeQueueList) {
const urlQueueListToSave = urlQueueListObj.urlQueueList.map(
(element: WebHistory) => {
const urlQueueListToSave = urlQueueListObj.urlQueueList.map((element: WebHistory) => {
if (element.tabsessionId !== tabId) {
return element
return element;
}
}
)
const timeQueueListSave = timeQueueListObj.timeQueueList.map(
(element: WebHistory) => {
});
const timeQueueListSave = timeQueueListObj.timeQueueList.map((element: WebHistory) => {
if (element.tabsessionId !== tabId) {
return element
return element;
}
}
)
});
await storage.set("urlQueueList", {
urlQueueList: urlQueueListToSave.filter((item: any) => item)
})
urlQueueList: urlQueueListToSave.filter((item: any) => item),
});
await storage.set("timeQueueList", {
timeQueueList: timeQueueListSave.filter((item: any) => item)
})
timeQueueList: timeQueueListSave.filter((item: any) => item),
});
}
})
});

View file

@ -1,21 +1,18 @@
import type { PlasmoMessaging } from "@plasmohq/messaging"
import { Storage } from "@plasmohq/storage"
import type { PlasmoMessaging } from "@plasmohq/messaging";
import { Storage } from "@plasmohq/storage";
import {
emptyArr,
webhistoryToLangChainDocument
} from "~utils/commons"
import { emptyArr, webhistoryToLangChainDocument } from "~utils/commons";
const clearMemory = async () => {
try {
const storage = new Storage({ area: "local" })
const storage = new Storage({ area: "local" });
let webHistory: any = await storage.get("webhistory")
let urlQueue: any = await storage.get("urlQueueList")
let timeQueue: any = await storage.get("timeQueueList")
const webHistory: any = await storage.get("webhistory");
const urlQueue: any = await storage.get("urlQueueList");
const timeQueue: any = await storage.get("timeQueueList");
if (!webHistory.webhistory) {
return
return;
}
//Main Cleanup COde
@ -24,69 +21,69 @@ const clearMemory = async () => {
// console.log("Event Tabs",tabs)
let actives = tabs.map((tab) => {
if (tab.id) {
return tab.id
return tab.id;
}
})
});
actives = actives.filter((item: any) => item)
actives = actives.filter((item: any) => item);
//Only retain which is still active
const newHistory = webHistory.webhistory.map((element: any) => {
//@ts-ignore
if (actives.includes(element.tabsessionId)) {
return element
return element;
}
})
});
const newUrlQueue = urlQueue.urlQueueList.map((element: any) => {
//@ts-ignore
if (actives.includes(element.tabsessionId)) {
return element
return element;
}
})
});
const newTimeQueue = timeQueue.timeQueueList.map((element: any) => {
//@ts-ignore
if (actives.includes(element.tabsessionId)) {
return element
return element;
}
})
});
await storage.set("webhistory", {
webhistory: newHistory.filter((item: any) => item)
})
webhistory: newHistory.filter((item: any) => item),
});
await storage.set("urlQueueList", {
urlQueueList: newUrlQueue.filter((item: any) => item)
})
urlQueueList: newUrlQueue.filter((item: any) => item),
});
await storage.set("timeQueueList", {
timeQueueList: newTimeQueue.filter((item: any) => item)
})
})
timeQueueList: newTimeQueue.filter((item: any) => item),
});
});
} catch (error) {
console.log(error)
}
console.log(error);
}
};
const handler: PlasmoMessaging.MessageHandler = async (req, res) => {
try {
const storage = new Storage({ area: "local" })
const storage = new Storage({ area: "local" });
const webhistoryObj: any = await storage.get("webhistory")
const webhistory = webhistoryObj.webhistory
const webhistoryObj: any = await storage.get("webhistory");
const webhistory = webhistoryObj.webhistory;
if (webhistory) {
let toSaveFinally: any[] = []
let newHistoryAfterCleanup: any[] = []
const toSaveFinally: any[] = [];
const newHistoryAfterCleanup: any[] = [];
for (let i = 0; i < webhistory.length; i++) {
const markdownFormat = webhistoryToLangChainDocument(
webhistory[i].tabsessionId,
webhistory[i].tabHistory
)
toSaveFinally.push(...markdownFormat)
);
toSaveFinally.push(...markdownFormat);
newHistoryAfterCleanup.push({
tabsessionId: webhistory[i].tabsessionId,
tabHistory: emptyArr
})
tabHistory: emptyArr,
});
}
await storage.set("webhistory", { webhistory: newHistoryAfterCleanup });
@ -97,16 +94,20 @@ const handler: PlasmoMessaging.MessageHandler = async (req, res) => {
}
// Create content array for documents in the format expected by the new API
const content = toSaveFinally.map(item => ({
const content = toSaveFinally.map((item) => ({
metadata: {
BrowsingSessionId: String(item.metadata.BrowsingSessionId || ""),
VisitedWebPageURL: String(item.metadata.VisitedWebPageURL || ""),
VisitedWebPageTitle: String(item.metadata.VisitedWebPageTitle || "No Title"),
VisitedWebPageDateWithTimeInISOString: String(item.metadata.VisitedWebPageDateWithTimeInISOString || ""),
VisitedWebPageDateWithTimeInISOString: String(
item.metadata.VisitedWebPageDateWithTimeInISOString || ""
),
VisitedWebPageReffererURL: String(item.metadata.VisitedWebPageReffererURL || ""),
VisitedWebPageVisitDurationInMilliseconds: String(item.metadata.VisitedWebPageVisitDurationInMilliseconds || "0")
VisitedWebPageVisitDurationInMilliseconds: String(
item.metadata.VisitedWebPageVisitDurationInMilliseconds || "0"
),
},
pageContent: String(item.pageContent || "")
pageContent: String(item.pageContent || ""),
}));
const token = await storage.get("token");
@ -115,35 +116,35 @@ const handler: PlasmoMessaging.MessageHandler = async (req, res) => {
const toSend = {
document_type: "EXTENSION",
content: content,
search_space_id: search_space_id
}
search_space_id: search_space_id,
};
console.log("toSend", toSend)
console.log("toSend", toSend);
const requestOptions = {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${token}`
Authorization: `Bearer ${token}`,
},
body: JSON.stringify(toSend)
}
body: JSON.stringify(toSend),
};
const response = await fetch(
`${process.env.PLASMO_PUBLIC_BACKEND_URL}/api/v1/documents/`,
requestOptions
)
const resp = await response.json()
);
const resp = await response.json();
if (resp) {
await clearMemory()
await clearMemory();
res.send({
message: "Save Job Started"
})
message: "Save Job Started",
});
}
}
} catch (error) {
console.log(error)
}
console.log(error);
}
};
export default handler
export default handler;

View file

@ -1,11 +1,10 @@
import { DOMParser } from "linkedom"
import type { PlasmoMessaging } from "@plasmohq/messaging";
import { Storage } from "@plasmohq/storage"
import type { PlasmoMessaging } from "@plasmohq/messaging"
import type { WebHistory } from "~utils/interfaces"
import { webhistoryToLangChainDocument, getRenderedHtml } from "~utils/commons"
import { convertHtmlToMarkdown } from "dom-to-semantic-markdown"
import { Storage } from "@plasmohq/storage";
import { convertHtmlToMarkdown } from "dom-to-semantic-markdown";
import { DOMParser } from "linkedom";
import { getRenderedHtml, webhistoryToLangChainDocument } from "~utils/commons";
import type { WebHistory } from "~utils/interfaces";
// @ts-ignore
global.Node = {
@ -22,25 +21,23 @@ global.Node = {
const handler: PlasmoMessaging.MessageHandler = async (req, res) => {
try {
chrome.tabs.query(
{ active: true, currentWindow: true },
async function (tabs) {
const storage = new Storage({ area: "local" })
const tab = tabs[0]
chrome.tabs.query({ active: true, currentWindow: true }, async (tabs) => {
const storage = new Storage({ area: "local" });
const tab = tabs[0];
if (tab.id) {
const tabId: number = tab.id
console.log("tabs", tabs)
const tabId: number = tab.id;
console.log("tabs", tabs);
const result = await chrome.scripting.executeScript({
// @ts-ignore
target: { tabId: tab.id },
// @ts-ignore
func: getRenderedHtml,
// world: "MAIN"
})
});
console.log("SnapRes", result)
console.log("SnapRes", result);
let toPushInTabHistory: any = result[0].result // const { renderedHtml, title, url, entryTime } = result[0].result;
const toPushInTabHistory: any = result[0].result; // const { renderedHtml, title, url, entryTime } = result[0].result;
toPushInTabHistory.pageContentMarkdown = convertHtmlToMarkdown(
toPushInTabHistory.renderedHtml,
@ -48,44 +45,41 @@ const handler: PlasmoMessaging.MessageHandler = async (req, res) => {
extractMainContent: true,
enableTableColumnTracking: true,
includeMetaData: false,
overrideDOMParser: new DOMParser()
overrideDOMParser: new DOMParser(),
}
)
);
delete toPushInTabHistory.renderedHtml
delete toPushInTabHistory.renderedHtml;
console.log("toPushInTabHistory", toPushInTabHistory)
console.log("toPushInTabHistory", toPushInTabHistory);
const urlQueueListObj: any = await storage.get("urlQueueList")
const timeQueueListObj: any = await storage.get("timeQueueList")
const urlQueueListObj: any = await storage.get("urlQueueList");
const timeQueueListObj: any = await storage.get("timeQueueList");
const isUrlQueueThere = urlQueueListObj.urlQueueList.find(
(data: WebHistory) => data.tabsessionId === tabId
)
);
const isTimeQueueThere = timeQueueListObj.timeQueueList.find(
(data: WebHistory) => data.tabsessionId === tabId
)
);
toPushInTabHistory.duration =
toPushInTabHistory.entryTime -
isTimeQueueThere.timeQueue[isTimeQueueThere.timeQueue.length - 1]
if (isUrlQueueThere.urlQueue.length == 1) {
toPushInTabHistory.reffererUrl = "START"
isTimeQueueThere.timeQueue[isTimeQueueThere.timeQueue.length - 1];
if (isUrlQueueThere.urlQueue.length === 1) {
toPushInTabHistory.reffererUrl = "START";
}
if (isUrlQueueThere.urlQueue.length > 1) {
toPushInTabHistory.reffererUrl =
isUrlQueueThere.urlQueue[isUrlQueueThere.urlQueue.length - 2]
isUrlQueueThere.urlQueue[isUrlQueueThere.urlQueue.length - 2];
}
let toSaveFinally: any[] = []
const toSaveFinally: any[] = [];
const markdownFormat = webhistoryToLangChainDocument(
tab.id,
[toPushInTabHistory]
)
toSaveFinally.push(...markdownFormat)
const markdownFormat = webhistoryToLangChainDocument(tab.id, [toPushInTabHistory]);
toSaveFinally.push(...markdownFormat);
console.log("toSaveFinally", toSaveFinally)
console.log("toSaveFinally", toSaveFinally);
// Log first item to debug metadata structure
if (toSaveFinally.length > 0) {
@ -94,16 +88,20 @@ const handler: PlasmoMessaging.MessageHandler = async (req, res) => {
// Create content array for documents in the format expected by the new API
// The metadata is already in the correct format in toSaveFinally
const content = toSaveFinally.map(item => ({
const content = toSaveFinally.map((item) => ({
metadata: {
BrowsingSessionId: String(item.metadata.BrowsingSessionId || ""),
VisitedWebPageURL: String(item.metadata.VisitedWebPageURL || ""),
VisitedWebPageTitle: String(item.metadata.VisitedWebPageTitle || "No Title"),
VisitedWebPageDateWithTimeInISOString: String(item.metadata.VisitedWebPageDateWithTimeInISOString || ""),
VisitedWebPageDateWithTimeInISOString: String(
item.metadata.VisitedWebPageDateWithTimeInISOString || ""
),
VisitedWebPageReffererURL: String(item.metadata.VisitedWebPageReffererURL || ""),
VisitedWebPageVisitDurationInMilliseconds: String(item.metadata.VisitedWebPageVisitDurationInMilliseconds || "0")
VisitedWebPageVisitDurationInMilliseconds: String(
item.metadata.VisitedWebPageVisitDurationInMilliseconds || "0"
),
},
pageContent: String(item.pageContent || "")
pageContent: String(item.pageContent || ""),
}));
const token = await storage.get("token");
@ -112,34 +110,33 @@ const handler: PlasmoMessaging.MessageHandler = async (req, res) => {
const toSend = {
document_type: "EXTENSION",
content: content,
search_space_id: search_space_id
}
search_space_id: search_space_id,
};
const requestOptions = {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${token}`
Authorization: `Bearer ${token}`,
},
body: JSON.stringify(toSend)
}
body: JSON.stringify(toSend),
};
const response = await fetch(
`${process.env.PLASMO_PUBLIC_BACKEND_URL}/api/v1/documents/`,
requestOptions
)
const resp = await response.json()
);
const resp = await response.json();
if (resp) {
res.send({
message: "Snapshot Saved Successfully"
})
message: "Snapshot Saved Successfully",
});
}
}
}
)
});
} catch (error) {
console.log(error)
}
console.log(error);
}
};
export default handler
export default handler;

View file

@ -0,0 +1,115 @@
{
"$schema": "https://biomejs.dev/schemas/2.1.2/schema.json",
"vcs": {
"enabled": true,
"clientKind": "git",
"useIgnoreFile": true
},
"files": {
"ignoreUnknown": true,
"experimentalScannerIgnores": ["node_modules", ".git", ".next", "dist", "build", "coverage"],
"maxSize": 1048576
},
"formatter": {
"enabled": true,
"indentStyle": "tab",
"indentWidth": 2,
"lineWidth": 100,
"lineEnding": "lf",
"formatWithErrors": false
},
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"suspicious": {
"noExplicitAny": "warn",
"noArrayIndexKey": "warn"
},
"style": {
"useConst": "error",
"useTemplate": "warn"
},
"correctness": {
"useExhaustiveDependencies": "warn"
}
}
},
"javascript": {
"formatter": {
"quoteStyle": "double",
"jsxQuoteStyle": "double",
"quoteProperties": "asNeeded",
"trailingCommas": "es5",
"semicolons": "always",
"arrowParentheses": "always",
"bracketSameLine": false,
"bracketSpacing": true
},
"linter": {
"enabled": true
},
"assist": {
"enabled": true
}
},
"json": {
"formatter": {
"enabled": true,
"indentStyle": "tab",
"indentWidth": 2,
"lineWidth": 100
},
"linter": {
"enabled": true
}
},
"css": {
"formatter": {
"enabled": true,
"indentStyle": "tab",
"indentWidth": 2,
"lineWidth": 100,
"quoteStyle": "double"
},
"linter": {
"enabled": true
}
},
"assist": {
"enabled": true,
"actions": {
"source": {
"organizeImports": "on"
}
}
},
"overrides": [
{
"includes": ["*.json", "*.jsonc"],
"json": {
"parser": {
"allowComments": true,
"allowTrailingCommas": false
}
}
},
{
"includes": [".vscode/**/*.json"],
"json": {
"parser": {
"allowComments": true,
"allowTrailingCommas": true
}
}
},
{
"includes": ["**/*.config.*", "**/next.config.*"],
"javascript": {
"formatter": {
"semicolons": "always"
}
}
}
]
}

View file

@ -1,8 +1,7 @@
import type { PlasmoCSConfig } from "plasmo"
import type { PlasmoCSConfig } from "plasmo";
export const config: PlasmoCSConfig = {
matches: ["<all_urls>"],
all_frames: true,
world: "MAIN"
}
world: "MAIN",
};

View file

@ -4,7 +4,8 @@
font-weight: 400;
font-display: swap;
src: url(data-base64:~assets/Fascinate.woff2) format("woff2");
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
unicode-range:
U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
U+FEFF, U+FFFD;
}

View file

@ -1,6 +1,6 @@
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
return twMerge(clsx(inputs));
}

View file

@ -14,6 +14,7 @@
"@plasmohq/storage": "^1.11.0",
"@radix-ui/react-dialog": "^1.1.2",
"@radix-ui/react-icons": "^1.3.2",
"@radix-ui/react-label": "^2.1.7",
"@radix-ui/react-popover": "^1.1.2",
"@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-toast": "^1.2.2",
@ -34,14 +35,13 @@
"tailwindcss-animate": "^1.0.7"
},
"devDependencies": {
"@ianvs/prettier-plugin-sort-imports": "4.1.1",
"@biomejs/biome": "2.1.2",
"@types/chrome": "0.0.258",
"@types/node": "20.11.5",
"@types/react": "18.2.48",
"@types/react-dom": "18.2.18",
"autoprefixer": "^10.4.20",
"postcss": "^8.4.41",
"prettier": "3.2.4",
"tailwindcss": "^3.4.10",
"typescript": "5.3.3"
},

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,6 @@
import { MemoryRouter } from "react-router-dom"
import { Routing } from "~routes"
import { Toaster } from "@/routes/ui/toaster"
import { MemoryRouter } from "react-router-dom";
import { Toaster } from "@/routes/ui/toaster";
import { Routing } from "~routes";
function IndexPopup() {
return (
@ -9,7 +8,7 @@ function IndexPopup() {
<Routing />
<Toaster />
</MemoryRouter>
)
);
}
export default IndexPopup
export default IndexPopup;

View file

@ -1,13 +1,12 @@
import { Route, Routes } from "react-router-dom"
import ApiKeyForm from "./pages/ApiKeyForm"
import HomePage from "./pages/HomePage"
import '../tailwind.css'
import { Route, Routes } from "react-router-dom";
import ApiKeyForm from "./pages/ApiKeyForm";
import HomePage from "./pages/HomePage";
import "../tailwind.css";
export const Routing = () => (
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/login" element={<ApiKeyForm />} />
</Routes>
)
);

View file

@ -1,27 +1,27 @@
import React, { useState } from "react";
import { useNavigate } from "react-router-dom"
import icon from "data-base64:~assets/icon.png"
import { Storage } from "@plasmohq/storage"
import { Button } from "~/routes/ui/button"
import { ReloadIcon } from "@radix-ui/react-icons"
import icon from "data-base64:~assets/icon.png";
import { Storage } from "@plasmohq/storage";
import { ReloadIcon } from "@radix-ui/react-icons";
import { useState } from "react";
import { useNavigate } from "react-router-dom";
import { Button } from "~/routes/ui/button";
const ApiKeyForm = () => {
const navigation = useNavigate()
const [apiKey, setApiKey] = useState('');
const [error, setError] = useState('');
const navigation = useNavigate();
const [apiKey, setApiKey] = useState("");
const [error, setError] = useState("");
const [loading, setLoading] = useState(false);
const storage = new Storage({ area: "local" })
const storage = new Storage({ area: "local" });
const validateForm = () => {
if (!apiKey) {
setError('API key is required');
setError("API key is required");
return false;
}
setError('');
setError("");
return true;
};
const handleSubmit = async (event: { preventDefault: () => void; }) => {
const handleSubmit = async (event: { preventDefault: () => void }) => {
event.preventDefault();
if (!validateForm()) return;
setLoading(true);
@ -29,24 +29,24 @@ const ApiKeyForm = () => {
try {
// Verify token is valid by making a request to the API
const response = await fetch(`${process.env.PLASMO_PUBLIC_BACKEND_URL}/verify-token`, {
method: 'GET',
method: "GET",
headers: {
'Authorization': `Bearer ${apiKey}`,
}
Authorization: `Bearer ${apiKey}`,
},
});
setLoading(false);
if (response.ok) {
// Store the API key as the token
await storage.set('token', apiKey);
navigation("/")
await storage.set("token", apiKey);
navigation("/");
} else {
setError('Invalid API key. Please check and try again.');
setError("Invalid API key. Please check and try again.");
}
} catch (error) {
setLoading(false);
setError('An error occurred. Please try again later.');
setError("An error occurred. Please try again later.");
}
};
@ -80,9 +80,7 @@ const ApiKeyForm = () => {
className="w-full px-3 py-2 bg-gray-900/50 border border-gray-700 rounded-md focus:outline-none focus:ring-2 focus:ring-teal-500 text-white placeholder:text-gray-500"
placeholder="Enter your API key"
/>
{error && (
<p className="text-red-400 text-sm mt-1">{error}</p>
)}
{error && <p className="text-red-400 text-sm mt-1">{error}</p>}
</div>
<Button
@ -108,6 +106,7 @@ const ApiKeyForm = () => {
href="https://www.surfsense.net"
target="_blank"
className="text-teal-400 hover:text-teal-300 hover:underline"
rel="noopener"
>
Sign up
</a>
@ -118,6 +117,6 @@ const ApiKeyForm = () => {
</div>
</div>
);
}
};
export default ApiKeyForm
export default ApiKeyForm;

View file

@ -1,16 +1,21 @@
import React, { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom"
import icon from "data-base64:~assets/icon.png"
import brain from "data-base64:~assets/brain.png";
import icon from "data-base64:~assets/icon.png";
import { sendToBackground } from "@plasmohq/messaging";
import { Storage } from "@plasmohq/storage";
import {
CrossCircledIcon,
DiscIcon,
ExitIcon,
FileIcon,
ReloadIcon,
UploadIcon,
} from "@radix-ui/react-icons";
import { convertHtmlToMarkdown } from "dom-to-semantic-markdown";
import type { WebHistory } from "~utils/interfaces";
import { getRenderedHtml } from "~utils/commons";
import Loading from "./Loading";
import brain from "data-base64:~assets/brain.png"
import { Storage } from "@plasmohq/storage"
import { sendToBackground } from "@plasmohq/messaging"
import { Check, ChevronsUpDown } from "lucide-react"
import { cn } from "~/lib/utils"
import { Button } from "~/routes/ui/button"
import { Check, ChevronsUpDown } from "lucide-react";
import React, { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import { cn } from "~/lib/utils";
import { Button } from "~/routes/ui/button";
import {
Command,
CommandEmpty,
@ -18,59 +23,49 @@ import {
CommandInput,
CommandItem,
CommandList,
} from "~/routes/ui/command"
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "~/routes/ui/popover"
} from "~/routes/ui/command";
import { Popover, PopoverContent, PopoverTrigger } from "~/routes/ui/popover";
import { Label } from "~routes/ui/label";
import { useToast } from "~routes/ui/use-toast";
import {
CircleIcon,
CrossCircledIcon,
DiscIcon,
ExitIcon,
FileIcon,
ReloadIcon,
ResetIcon,
UploadIcon
} from "@radix-ui/react-icons"
import { getRenderedHtml } from "~utils/commons";
import type { WebHistory } from "~utils/interfaces";
import Loading from "./Loading";
const HomePage = () => {
const { toast } = useToast()
const navigation = useNavigate()
const { toast } = useToast();
const navigation = useNavigate();
const [noOfWebPages, setNoOfWebPages] = useState<number>(0);
const [loading, setLoading] = useState(true);
const [open, setOpen] = React.useState(false)
const [value, setValue] = React.useState<string>("")
const [searchspaces, setSearchSpaces] = useState([])
const [open, setOpen] = React.useState(false);
const [value, setValue] = React.useState<string>("");
const [searchspaces, setSearchSpaces] = useState([]);
const [isSaving, setIsSaving] = useState(false);
useEffect(() => {
const checkSearchSpaces = async () => {
const storage = new Storage({ area: "local" })
const token = await storage.get('token');
const storage = new Storage({ area: "local" });
const token = await storage.get("token");
try {
const response = await fetch(
`${process.env.PLASMO_PUBLIC_BACKEND_URL}/api/v1/searchspaces/`,
{
headers: {
'Authorization': `Bearer ${token}`
}
Authorization: `Bearer ${token}`,
},
}
);
if (!response.ok) {
throw new Error("Token verification failed");
} else {
const res = await response.json()
console.log(res)
setSearchSpaces(res)
const res = await response.json();
console.log(res);
setSearchSpaces(res);
}
} catch (error) {
await storage.remove('token');
await storage.remove('showShadowDom');
navigation("/login")
await storage.remove("token");
await storage.remove("showShadowDom");
navigation("/login");
}
};
@ -78,67 +73,64 @@ const HomePage = () => {
setLoading(false);
}, []);
useEffect(() => {
async function onLoad() {
try {
chrome.storage.onChanged.addListener(
(changes: any, areaName: string) => {
chrome.storage.onChanged.addListener((changes: any, areaName: string) => {
if (changes.webhistory) {
const webhistory = JSON.parse(changes.webhistory.newValue);
console.log("webhistory", webhistory)
console.log("webhistory", webhistory);
let sum = 0
let sum = 0;
webhistory.webhistory.forEach((element: any) => {
sum = sum + element.tabHistory.length
sum = sum + element.tabHistory.length;
});
setNoOfWebPages(sum)
setNoOfWebPages(sum);
}
}
);
});
const storage = new Storage({ area: "local" })
const storage = new Storage({ area: "local" });
const searchspace = await storage.get("search_space");
if (searchspace) {
setValue(searchspace)
setValue(searchspace);
}
await storage.set("showShadowDom", true)
await storage.set("showShadowDom", true);
const webhistoryObj: any = await storage.get("webhistory");
if (webhistoryObj.webhistory.length) {
const webhistory = webhistoryObj.webhistory;
if (webhistoryObj) {
let sum = 0
let sum = 0;
webhistory.forEach((element: any) => {
sum = sum + element.tabHistory.length
sum = sum + element.tabHistory.length;
});
setNoOfWebPages(sum)
setNoOfWebPages(sum);
}
} else {
setNoOfWebPages(0)
setNoOfWebPages(0);
}
} catch (error) {
console.log(error);
}
}
onLoad()
onLoad();
}, []);
async function clearMem(): Promise<void> {
try {
const storage = new Storage({ area: "local" })
const storage = new Storage({ area: "local" });
let webHistory: any = await storage.get("webhistory");
let urlQueue: any = await storage.get("urlQueueList");
let timeQueue: any = await storage.get("timeQueueList");
const webHistory: any = await storage.get("webhistory");
const urlQueue: any = await storage.get("urlQueueList");
const timeQueue: any = await storage.get("timeQueueList");
if (!webHistory.webhistory) {
return
return;
}
//Main Cleanup COde
@ -146,42 +138,46 @@ const HomePage = () => {
//Get Active Tabs Ids
let actives = tabs.map((tab) => {
if (tab.id) {
return tab.id
return tab.id;
}
})
});
actives = actives.filter((item: any) => item)
actives = actives.filter((item: any) => item);
//Only retain which is still active
const newHistory = webHistory.webhistory.map((element: any) => {
//@ts-ignore
if (actives.includes(element.tabsessionId)) {
return element
return element;
}
})
});
const newUrlQueue = urlQueue.urlQueueList.map((element: any) => {
//@ts-ignore
if (actives.includes(element.tabsessionId)) {
return element
return element;
}
})
});
const newTimeQueue = timeQueue.timeQueueList.map((element: any) => {
//@ts-ignore
if (actives.includes(element.tabsessionId)) {
return element
return element;
}
})
});
await storage.set("webhistory", { webhistory: newHistory.filter((item: any) => item) });
await storage.set("urlQueueList", { urlQueueList: newUrlQueue.filter((item: any) => item) });
await storage.set("timeQueueList", { timeQueueList: newTimeQueue.filter((item: any) => item) });
await storage.set("urlQueueList", {
urlQueueList: newUrlQueue.filter((item: any) => item),
});
await storage.set("timeQueueList", {
timeQueueList: newTimeQueue.filter((item: any) => item),
});
toast({
title: "History store cleared",
description: "Inactive history sessions have been removed",
variant: "destructive",
})
});
});
} catch (error) {
console.log(error);
@ -189,11 +185,11 @@ const HomePage = () => {
}
async function saveCurrSnapShot(): Promise<void> {
chrome.tabs.query({ active: true, currentWindow: true }, async function (tabs) {
const storage = new Storage({ area: "local" })
chrome.tabs.query({ active: true, currentWindow: true }, async (tabs) => {
const storage = new Storage({ area: "local" });
const tab = tabs[0];
if (tab.id) {
const tabId: number = tab.id
const tabId: number = tab.id;
const result = await chrome.scripting.executeScript({
// @ts-ignore
target: { tabId: tab.id },
@ -201,42 +197,47 @@ const HomePage = () => {
func: getRenderedHtml,
});
let toPushInTabHistory: any = result[0].result;
const toPushInTabHistory: any = result[0].result;
//Updates 'tabhistory'
let webhistoryObj: any = await storage.get("webhistory");
const webhistoryObj: any = await storage.get("webhistory");
const webHistoryOfTabId = webhistoryObj.webhistory.filter(
(data: WebHistory) => {
const webHistoryOfTabId = webhistoryObj.webhistory.filter((data: WebHistory) => {
return data.tabsessionId === tab.id;
}
);
});
toPushInTabHistory.pageContentMarkdown = convertHtmlToMarkdown(
toPushInTabHistory.renderedHtml,
{
extractMainContent: true,
includeMetaData: false,
enableTableColumnTracking: true
enableTableColumnTracking: true,
}
)
);
delete toPushInTabHistory.renderedHtml
delete toPushInTabHistory.renderedHtml;
let tabhistory = webHistoryOfTabId[0].tabHistory;
const tabhistory = webHistoryOfTabId[0].tabHistory;
const urlQueueListObj: any = await storage.get("urlQueueList");
const timeQueueListObj: any = await storage.get("timeQueueList");
const isUrlQueueThere = urlQueueListObj.urlQueueList.find((data: WebHistory) => data.tabsessionId === tabId)
const isTimeQueueThere = timeQueueListObj.timeQueueList.find((data: WebHistory) => data.tabsessionId === tabId)
const isUrlQueueThere = urlQueueListObj.urlQueueList.find(
(data: WebHistory) => data.tabsessionId === tabId
);
const isTimeQueueThere = timeQueueListObj.timeQueueList.find(
(data: WebHistory) => data.tabsessionId === tabId
);
toPushInTabHistory.duration = toPushInTabHistory.entryTime - isTimeQueueThere.timeQueue[isTimeQueueThere.timeQueue.length - 1]
if (isUrlQueueThere.urlQueue.length == 1) {
toPushInTabHistory.reffererUrl = 'START'
toPushInTabHistory.duration =
toPushInTabHistory.entryTime -
isTimeQueueThere.timeQueue[isTimeQueueThere.timeQueue.length - 1];
if (isUrlQueueThere.urlQueue.length === 1) {
toPushInTabHistory.reffererUrl = "START";
}
if (isUrlQueueThere.urlQueue.length > 1) {
toPushInTabHistory.reffererUrl = isUrlQueueThere.urlQueue[isUrlQueueThere.urlQueue.length - 2];
toPushInTabHistory.reffererUrl =
isUrlQueueThere.urlQueue[isUrlQueueThere.urlQueue.length - 2];
}
webHistoryOfTabId[0].tabHistory.push(toPushInTabHistory);
@ -246,9 +247,8 @@ const HomePage = () => {
toast({
title: "Snapshot saved",
description: `Captured: ${toPushInTabHistory.title}`,
})
});
}
});
}
@ -256,52 +256,52 @@ const HomePage = () => {
if (value === "") {
toast({
title: "Select a SearchSpace !",
})
return
});
return;
}
const storage = new Storage({ area: "local" })
const storage = new Storage({ area: "local" });
const search_space_id = await storage.get("search_space_id");
if (!search_space_id) {
toast({
title: "Invalid SearchSpace selected!",
variant: "destructive",
})
return
});
return;
}
setIsSaving(true);
toast({
title: "Save job running",
description: "Saving captured content to SurfSense",
})
});
try {
const resp = await sendToBackground({
// @ts-ignore
name: "savedata",
})
});
toast({
title: resp.message,
})
});
} catch (error) {
toast({
title: "Error saving data",
description: "Please try again",
variant: "destructive",
})
});
} finally {
setIsSaving(false);
}
}
};
async function logOut(): Promise<void> {
const storage = new Storage({ area: "local" })
await storage.remove('token');
await storage.remove('showShadowDom');
navigation("/login")
const storage = new Storage({ area: "local" });
await storage.remove("token");
await storage.remove("showShadowDom");
navigation("/login");
}
if (loading) {
@ -367,14 +367,11 @@ const HomePage = () => {
</div>
<div className="rounded-lg border border-gray-700 bg-gray-800/50 p-4 backdrop-blur-sm">
<label className="mb-2 block text-sm font-medium text-gray-300">
Search Space
</label>
<Label className="mb-2 block text-sm font-medium text-gray-300">Search Space</Label>
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button
variant="outline"
role="combobox"
aria-expanded={open}
className="w-full justify-between border-gray-700 bg-gray-900 text-white hover:bg-gray-700"
>
@ -386,7 +383,10 @@ const HomePage = () => {
</PopoverTrigger>
<PopoverContent className="w-full border-gray-700 bg-gray-800/90 p-0 backdrop-blur-sm">
<Command className="bg-transparent">
<CommandInput placeholder="Search spaces..." className="border-gray-700 bg-gray-900 text-gray-200" />
<CommandInput
placeholder="Search spaces..."
className="border-gray-700 bg-gray-900 text-gray-200"
/>
<CommandList>
<CommandEmpty>No search spaces found.</CommandEmpty>
<CommandGroup>
@ -395,17 +395,19 @@ const HomePage = () => {
key={space.name}
value={space.name}
onSelect={async (currentValue) => {
const storage = new Storage({ area: "local" })
const storage = new Storage({ area: "local" });
if (currentValue === value) {
await storage.set("search_space", "");
await storage.set("search_space_id", 0);
} else {
const selectedSpace = searchspaces.find((space) => space.name === currentValue);
const selectedSpace = searchspaces.find(
(space) => space.name === currentValue
);
await storage.set("search_space", currentValue);
await storage.set("search_space_id", selectedSpace.id);
}
setValue(currentValue === value ? "" : currentValue)
setOpen(false)
setValue(currentValue === value ? "" : currentValue);
setOpen(false);
}}
className="aria-selected:bg-gray-700"
>
@ -473,4 +475,4 @@ const HomePage = () => {
}
};
export default HomePage
export default HomePage;

View file

@ -1,6 +1,5 @@
import React from 'react'
import icon from "data-base64:~assets/icon.png"
import { ReloadIcon } from "@radix-ui/react-icons"
import icon from "data-base64:~assets/icon.png";
import { ReloadIcon } from "@radix-ui/react-icons";
const Loading = () => {
return (
@ -22,7 +21,7 @@ const Loading = () => {
className="inline-block animate-pulse text-teal-400"
style={{
animationDelay: `${i * 0.1}s`,
animationDuration: '1.5s'
animationDuration: "1.5s",
}}
>
{letter}
@ -32,7 +31,7 @@ const Loading = () => {
</div>
</div>
</div>
)
}
);
};
export default Loading
export default Loading;

View file

@ -1,8 +1,8 @@
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority"
import { Slot } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "class-variance-authority";
import * as React from "react";
import { cn } from "~/lib/utils"
import { cn } from "~/lib/utils";
const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
@ -10,12 +10,9 @@ const buttonVariants = cva(
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
destructive:
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline:
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
secondary:
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
@ -31,26 +28,22 @@ const buttonVariants = cva(
size: "default",
},
}
)
);
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean
asChild?: boolean;
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button"
const Comp = asChild ? Slot : "button";
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
/>
)
<Comp className={cn(buttonVariants({ variant, size, className }))} ref={ref} {...props} />
);
}
)
Button.displayName = "Button"
);
Button.displayName = "Button";
export { Button, buttonVariants }
export { Button, buttonVariants };

View file

@ -1,12 +1,12 @@
"use client"
"use client";
import * as React from "react"
import { type DialogProps } from "@radix-ui/react-dialog"
import { Command as CommandPrimitive } from "cmdk"
import { Search } from "lucide-react"
import type { DialogProps } from "@radix-ui/react-dialog";
import { Command as CommandPrimitive } from "cmdk";
import { Search } from "lucide-react";
import * as React from "react";
import { cn } from "~/lib/utils"
import { Dialog, DialogContent } from "~/routes/ui/dialog"
import { cn } from "~/lib/utils";
import { Dialog, DialogContent } from "~/routes/ui/dialog";
const Command = React.forwardRef<
React.ElementRef<typeof CommandPrimitive>,
@ -20,8 +20,8 @@ const Command = React.forwardRef<
)}
{...props}
/>
))
Command.displayName = CommandPrimitive.displayName
));
Command.displayName = CommandPrimitive.displayName;
interface CommandDialogProps extends DialogProps {}
@ -34,8 +34,8 @@ const CommandDialog = ({ children, ...props }: CommandDialogProps) => {
</Command>
</DialogContent>
</Dialog>
)
}
);
};
const CommandInput = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Input>,
@ -52,9 +52,9 @@ const CommandInput = React.forwardRef<
{...props}
/>
</div>
))
));
CommandInput.displayName = CommandPrimitive.Input.displayName
CommandInput.displayName = CommandPrimitive.Input.displayName;
const CommandList = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.List>,
@ -65,22 +65,18 @@ const CommandList = React.forwardRef<
className={cn("max-h-[300px] overflow-y-auto overflow-x-hidden", className)}
{...props}
/>
))
));
CommandList.displayName = CommandPrimitive.List.displayName
CommandList.displayName = CommandPrimitive.List.displayName;
const CommandEmpty = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Empty>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Empty>
>((props, ref) => (
<CommandPrimitive.Empty
ref={ref}
className="py-6 text-center text-sm"
{...props}
/>
))
<CommandPrimitive.Empty ref={ref} className="py-6 text-center text-sm" {...props} />
));
CommandEmpty.displayName = CommandPrimitive.Empty.displayName
CommandEmpty.displayName = CommandPrimitive.Empty.displayName;
const CommandGroup = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Group>,
@ -94,9 +90,9 @@ const CommandGroup = React.forwardRef<
)}
{...props}
/>
))
));
CommandGroup.displayName = CommandPrimitive.Group.displayName
CommandGroup.displayName = CommandPrimitive.Group.displayName;
const CommandSeparator = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Separator>,
@ -107,8 +103,8 @@ const CommandSeparator = React.forwardRef<
className={cn("-mx-1 h-px bg-border", className)}
{...props}
/>
))
CommandSeparator.displayName = CommandPrimitive.Separator.displayName
));
CommandSeparator.displayName = CommandPrimitive.Separator.displayName;
const CommandItem = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Item>,
@ -122,25 +118,19 @@ const CommandItem = React.forwardRef<
)}
{...props}
/>
))
));
CommandItem.displayName = CommandPrimitive.Item.displayName
CommandItem.displayName = CommandPrimitive.Item.displayName;
const CommandShortcut = ({
className,
...props
}: React.HTMLAttributes<HTMLSpanElement>) => {
const CommandShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => {
return (
<span
className={cn(
"ml-auto text-xs tracking-widest text-muted-foreground",
className
)}
className={cn("ml-auto text-xs tracking-widest text-muted-foreground", className)}
{...props}
/>
)
}
CommandShortcut.displayName = "CommandShortcut"
);
};
CommandShortcut.displayName = "CommandShortcut";
export {
Command,
@ -152,4 +142,4 @@ export {
CommandItem,
CommandShortcut,
CommandSeparator,
}
};

View file

@ -1,18 +1,18 @@
"use client"
"use client";
import * as React from "react"
import * as DialogPrimitive from "@radix-ui/react-dialog"
import { X } from "lucide-react"
import * as DialogPrimitive from "@radix-ui/react-dialog";
import { X } from "lucide-react";
import * as React from "react";
import { cn } from "~/lib/utils"
import { cn } from "~/lib/utils";
const Dialog = DialogPrimitive.Root
const Dialog = DialogPrimitive.Root;
const DialogTrigger = DialogPrimitive.Trigger
const DialogTrigger = DialogPrimitive.Trigger;
const DialogPortal = DialogPrimitive.Portal
const DialogPortal = DialogPrimitive.Portal;
const DialogClose = DialogPrimitive.Close
const DialogClose = DialogPrimitive.Close;
const DialogOverlay = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Overlay>,
@ -26,8 +26,8 @@ const DialogOverlay = React.forwardRef<
)}
{...props}
/>
))
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName
));
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
const DialogContent = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Content>,
@ -50,36 +50,21 @@ const DialogContent = React.forwardRef<
</DialogPrimitive.Close>
</DialogPrimitive.Content>
</DialogPortal>
))
DialogContent.displayName = DialogPrimitive.Content.displayName
));
DialogContent.displayName = DialogPrimitive.Content.displayName;
const DialogHeader = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
const DialogHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
<div className={cn("flex flex-col space-y-1.5 text-center sm:text-left", className)} {...props} />
);
DialogHeader.displayName = "DialogHeader";
const DialogFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col space-y-1.5 text-center sm:text-left",
className
)}
className={cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className)}
{...props}
/>
)
DialogHeader.displayName = "DialogHeader"
const DialogFooter = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
className
)}
{...props}
/>
)
DialogFooter.displayName = "DialogFooter"
);
DialogFooter.displayName = "DialogFooter";
const DialogTitle = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Title>,
@ -87,14 +72,11 @@ const DialogTitle = React.forwardRef<
>(({ className, ...props }, ref) => (
<DialogPrimitive.Title
ref={ref}
className={cn(
"text-lg font-semibold leading-none tracking-tight",
className
)}
className={cn("text-lg font-semibold leading-none tracking-tight", className)}
{...props}
/>
))
DialogTitle.displayName = DialogPrimitive.Title.displayName
));
DialogTitle.displayName = DialogPrimitive.Title.displayName;
const DialogDescription = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Description>,
@ -105,8 +87,8 @@ const DialogDescription = React.forwardRef<
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
))
DialogDescription.displayName = DialogPrimitive.Description.displayName
));
DialogDescription.displayName = DialogPrimitive.Description.displayName;
export {
Dialog,
@ -119,4 +101,4 @@ export {
DialogFooter,
DialogTitle,
DialogDescription,
}
};

View file

@ -0,0 +1,21 @@
"use client";
import * as LabelPrimitive from "@radix-ui/react-label";
import type * as React from "react";
import { cn } from "@/lib/utils";
function Label({ className, ...props }: React.ComponentProps<typeof LabelPrimitive.Root>) {
return (
<LabelPrimitive.Root
data-slot="label"
className={cn(
"flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
className
)}
{...props}
/>
);
}
export { Label };

View file

@ -1,13 +1,13 @@
"use client"
"use client";
import * as React from "react"
import * as PopoverPrimitive from "@radix-ui/react-popover"
import * as PopoverPrimitive from "@radix-ui/react-popover";
import * as React from "react";
import { cn } from "~/lib/utils"
import { cn } from "~/lib/utils";
const Popover = PopoverPrimitive.Root
const Popover = PopoverPrimitive.Root;
const PopoverTrigger = PopoverPrimitive.Trigger
const PopoverTrigger = PopoverPrimitive.Trigger;
const PopoverContent = React.forwardRef<
React.ElementRef<typeof PopoverPrimitive.Content>,
@ -25,7 +25,7 @@ const PopoverContent = React.forwardRef<
{...props}
/>
</PopoverPrimitive.Portal>
))
PopoverContent.displayName = PopoverPrimitive.Content.displayName
));
PopoverContent.displayName = PopoverPrimitive.Content.displayName;
export { Popover, PopoverTrigger, PopoverContent }
export { Popover, PopoverTrigger, PopoverContent };

View file

@ -1,13 +1,13 @@
"use client"
"use client";
import * as React from "react"
import * as ToastPrimitives from "@radix-ui/react-toast"
import { cva, type VariantProps } from "class-variance-authority"
import { X } from "lucide-react"
import * as ToastPrimitives from "@radix-ui/react-toast";
import { cva, type VariantProps } from "class-variance-authority";
import { X } from "lucide-react";
import * as React from "react";
import { cn } from "@/lib/utils"
import { cn } from "@/lib/utils";
const ToastProvider = ToastPrimitives.Provider
const ToastProvider = ToastPrimitives.Provider;
const ToastViewport = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Viewport>,
@ -21,8 +21,8 @@ const ToastViewport = React.forwardRef<
)}
{...props}
/>
))
ToastViewport.displayName = ToastPrimitives.Viewport.displayName
));
ToastViewport.displayName = ToastPrimitives.Viewport.displayName;
const toastVariants = cva(
"group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full",
@ -38,12 +38,11 @@ const toastVariants = cva(
variant: "default",
},
}
)
);
const Toast = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Root>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> &
VariantProps<typeof toastVariants>
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> & VariantProps<typeof toastVariants>
>(({ className, variant, ...props }, ref) => {
return (
<ToastPrimitives.Root
@ -51,9 +50,9 @@ const Toast = React.forwardRef<
className={cn(toastVariants({ variant }), className)}
{...props}
/>
)
})
Toast.displayName = ToastPrimitives.Root.displayName
);
});
Toast.displayName = ToastPrimitives.Root.displayName;
const ToastAction = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Action>,
@ -67,8 +66,8 @@ const ToastAction = React.forwardRef<
)}
{...props}
/>
))
ToastAction.displayName = ToastPrimitives.Action.displayName
));
ToastAction.displayName = ToastPrimitives.Action.displayName;
const ToastClose = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Close>,
@ -85,20 +84,16 @@ const ToastClose = React.forwardRef<
>
<X className="h-4 w-4" />
</ToastPrimitives.Close>
))
ToastClose.displayName = ToastPrimitives.Close.displayName
));
ToastClose.displayName = ToastPrimitives.Close.displayName;
const ToastTitle = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Title>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Title>
>(({ className, ...props }, ref) => (
<ToastPrimitives.Title
ref={ref}
className={cn("text-sm font-semibold", className)}
{...props}
/>
))
ToastTitle.displayName = ToastPrimitives.Title.displayName
<ToastPrimitives.Title ref={ref} className={cn("text-sm font-semibold", className)} {...props} />
));
ToastTitle.displayName = ToastPrimitives.Title.displayName;
const ToastDescription = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Description>,
@ -109,12 +104,12 @@ const ToastDescription = React.forwardRef<
className={cn("text-sm opacity-90", className)}
{...props}
/>
))
ToastDescription.displayName = ToastPrimitives.Description.displayName
));
ToastDescription.displayName = ToastPrimitives.Description.displayName;
type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>
type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>;
type ToastActionElement = React.ReactElement<typeof ToastAction>
type ToastActionElement = React.ReactElement<typeof ToastAction>;
export {
type ToastProps,
@ -126,4 +121,4 @@ export {
ToastDescription,
ToastClose,
ToastAction,
}
};

View file

@ -1,6 +1,5 @@
"use client"
"use client";
import { useToast } from "@/routes/ui/use-toast"
import {
Toast,
ToastClose,
@ -8,28 +7,25 @@ import {
ToastProvider,
ToastTitle,
ToastViewport,
} from "@/routes/ui/toast"
} from "@/routes/ui/toast";
import { useToast } from "@/routes/ui/use-toast";
export function Toaster() {
const { toasts } = useToast()
const { toasts } = useToast();
return (
<ToastProvider>
{toasts.map(function ({ id, title, description, action, ...props }) {
return (
{toasts.map(({ id, title, description, action, ...props }) => (
<Toast key={id} {...props}>
<div className="grid gap-1">
{title && <ToastTitle>{title}</ToastTitle>}
{description && (
<ToastDescription>{description}</ToastDescription>
)}
{description && <ToastDescription>{description}</ToastDescription>}
</div>
{action}
<ToastClose />
</Toast>
)
})}
))}
<ToastViewport />
</ToastProvider>
)
);
}

View file

@ -1,78 +1,75 @@
"use client"
"use client";
// Inspired by react-hot-toast library
import * as React from "react"
import * as React from "react";
import type {
ToastActionElement,
ToastProps,
} from "@/routes/ui/toast"
import type { ToastActionElement, ToastProps } from "@/routes/ui/toast";
const TOAST_LIMIT = 1
const TOAST_REMOVE_DELAY = 1000000
const TOAST_LIMIT = 1;
const TOAST_REMOVE_DELAY = 1000000;
type ToasterToast = ToastProps & {
id: string
title?: React.ReactNode
description?: React.ReactNode
action?: ToastActionElement
}
id: string;
title?: React.ReactNode;
description?: React.ReactNode;
action?: ToastActionElement;
};
const actionTypes = {
ADD_TOAST: "ADD_TOAST",
UPDATE_TOAST: "UPDATE_TOAST",
DISMISS_TOAST: "DISMISS_TOAST",
REMOVE_TOAST: "REMOVE_TOAST",
} as const
} as const;
let count = 0
let count = 0;
function genId() {
count = (count + 1) % Number.MAX_SAFE_INTEGER
return count.toString()
count = (count + 1) % Number.MAX_SAFE_INTEGER;
return count.toString();
}
type ActionType = typeof actionTypes
type ActionType = typeof actionTypes;
type Action =
| {
type: ActionType["ADD_TOAST"]
toast: ToasterToast
type: ActionType["ADD_TOAST"];
toast: ToasterToast;
}
| {
type: ActionType["UPDATE_TOAST"]
toast: Partial<ToasterToast>
type: ActionType["UPDATE_TOAST"];
toast: Partial<ToasterToast>;
}
| {
type: ActionType["DISMISS_TOAST"]
toastId?: ToasterToast["id"]
type: ActionType["DISMISS_TOAST"];
toastId?: ToasterToast["id"];
}
| {
type: ActionType["REMOVE_TOAST"]
toastId?: ToasterToast["id"]
}
type: ActionType["REMOVE_TOAST"];
toastId?: ToasterToast["id"];
};
interface State {
toasts: ToasterToast[]
toasts: ToasterToast[];
}
const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>()
const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>();
const addToRemoveQueue = (toastId: string) => {
if (toastTimeouts.has(toastId)) {
return
return;
}
const timeout = setTimeout(() => {
toastTimeouts.delete(toastId)
toastTimeouts.delete(toastId);
dispatch({
type: "REMOVE_TOAST",
toastId: toastId,
})
}, TOAST_REMOVE_DELAY)
});
}, TOAST_REMOVE_DELAY);
toastTimeouts.set(toastId, timeout)
}
toastTimeouts.set(toastId, timeout);
};
export const reducer = (state: State, action: Action): State => {
switch (action.type) {
@ -80,27 +77,25 @@ export const reducer = (state: State, action: Action): State => {
return {
...state,
toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
}
};
case "UPDATE_TOAST":
return {
...state,
toasts: state.toasts.map((t) =>
t.id === action.toast.id ? { ...t, ...action.toast } : t
),
}
toasts: state.toasts.map((t) => (t.id === action.toast.id ? { ...t, ...action.toast } : t)),
};
case "DISMISS_TOAST": {
const { toastId } = action
const { toastId } = action;
// ! Side effects ! - This could be extracted into a dismissToast() action,
// but I'll keep it here for simplicity
if (toastId) {
addToRemoveQueue(toastId)
addToRemoveQueue(toastId);
} else {
state.toasts.forEach((toast) => {
addToRemoveQueue(toast.id)
})
addToRemoveQueue(toast.id);
});
}
return {
@ -113,44 +108,44 @@ export const reducer = (state: State, action: Action): State => {
}
: t
),
}
};
}
case "REMOVE_TOAST":
if (action.toastId === undefined) {
return {
...state,
toasts: [],
}
};
}
return {
...state,
toasts: state.toasts.filter((t) => t.id !== action.toastId),
};
}
}
}
};
const listeners: Array<(state: State) => void> = []
const listeners: Array<(state: State) => void> = [];
let memoryState: State = { toasts: [] }
let memoryState: State = { toasts: [] };
function dispatch(action: Action) {
memoryState = reducer(memoryState, action)
memoryState = reducer(memoryState, action);
listeners.forEach((listener) => {
listener(memoryState)
})
listener(memoryState);
});
}
type Toast = Omit<ToasterToast, "id">
type Toast = Omit<ToasterToast, "id">;
function toast({ ...props }: Toast) {
const id = genId()
const id = genId();
const update = (props: ToasterToast) =>
dispatch({
type: "UPDATE_TOAST",
toast: { ...props, id },
})
const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id })
});
const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id });
dispatch({
type: "ADD_TOAST",
@ -159,36 +154,36 @@ function toast({ ...props }: Toast) {
id,
open: true,
onOpenChange: (open) => {
if (!open) dismiss()
if (!open) dismiss();
},
},
})
});
return {
id: id,
dismiss,
update,
}
};
}
function useToast() {
const [state, setState] = React.useState<State>(memoryState)
const [state, setState] = React.useState<State>(memoryState);
React.useEffect(() => {
listeners.push(setState)
listeners.push(setState);
return () => {
const index = listeners.indexOf(setState)
const index = listeners.indexOf(setState);
if (index > -1) {
listeners.splice(index, 1)
listeners.splice(index, 1);
}
}
}, [state])
};
}, [state]);
return {
...state,
toast,
dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),
}
};
}
export { useToast, toast }
export { useToast, toast };

View file

@ -1,4 +1,4 @@
const { fontFamily } = require("tailwindcss/defaultTheme")
const { fontFamily } = require("tailwindcss/defaultTheme");
/** @type {import('tailwindcss').Config} */
module.exports = {
@ -73,4 +73,4 @@ module.exports = {
},
},
plugins: [require("tailwindcss-animate")],
}
};

View file

@ -80,8 +80,9 @@ body {
}
body {
@apply bg-background text-foreground;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen,
Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
font-family:
-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans",
"Helvetica Neue", sans-serif;
}
/* Styling for shadcn/ui components */

View file

@ -1,18 +1,10 @@
{
"extends": "plasmo/templates/tsconfig.base",
"exclude": [
"node_modules"
],
"include": [
".plasmo/index.d.ts",
"./**/*.ts",
"./**/*.tsx"
],
"extends": "plasmo/templates/tsconfig.base.json",
"exclude": ["node_modules"],
"include": [".plasmo/index.d.ts", "./**/*.ts", "./**/*.tsx"],
"compilerOptions": {
"paths": {
"~*": [
"./*"
],
"~*": ["./*"],
"@/*": ["./*"]
},
"baseUrl": "."

View file

@ -1,104 +1,100 @@
import { Storage } from "@plasmohq/storage"
import type { WebHistory } from "./interfaces"
import { Storage } from "@plasmohq/storage";
import type { WebHistory } from "./interfaces";
export const emptyArr: any[] = []
export const emptyArr: any[] = [];
export const initQueues = async (tabId: number) => {
const storage = new Storage({ area: "local" })
const storage = new Storage({ area: "local" });
let urlQueueListObj: any = await storage.get("urlQueueList")
let timeQueueListObj: any = await storage.get("timeQueueList")
const urlQueueListObj: any = await storage.get("urlQueueList");
const timeQueueListObj: any = await storage.get("timeQueueList");
if (!urlQueueListObj && !timeQueueListObj) {
await storage.set("urlQueueList", {
urlQueueList: [{ tabsessionId: tabId, urlQueue: [] }]
})
urlQueueList: [{ tabsessionId: tabId, urlQueue: [] }],
});
await storage.set("timeQueueList", {
timeQueueList: [{ tabsessionId: tabId, timeQueue: [] }]
})
timeQueueList: [{ tabsessionId: tabId, timeQueue: [] }],
});
return
return;
}
if (urlQueueListObj.urlQueueList && timeQueueListObj.timeQueueList) {
const isUrlQueueThere = urlQueueListObj.urlQueueList.find(
(data: WebHistory) => data.tabsessionId === tabId
)
);
const isTimeQueueThere = timeQueueListObj.timeQueueList.find(
(data: WebHistory) => data.tabsessionId === tabId
)
);
if (!isUrlQueueThere) {
urlQueueListObj.urlQueueList.push({ tabsessionId: tabId, urlQueue: [] })
urlQueueListObj.urlQueueList.push({ tabsessionId: tabId, urlQueue: [] });
await storage.set("urlQueueList", {
urlQueueList: urlQueueListObj.urlQueueList
})
urlQueueList: urlQueueListObj.urlQueueList,
});
}
if (!isTimeQueueThere) {
timeQueueListObj.timeQueueList.push({
tabsessionId: tabId,
timeQueue: []
})
timeQueue: [],
});
await storage.set("timeQueueList", {
timeQueueList: timeQueueListObj.timeQueueList
})
timeQueueList: timeQueueListObj.timeQueueList,
});
}
return
}
return;
}
};
export function getRenderedHtml() {
return {
url: window.location.href,
entryTime: Date.now(),
title: document.title,
renderedHtml: document.documentElement.outerHTML
}
renderedHtml: document.documentElement.outerHTML,
};
}
export const initWebHistory = async (tabId: number) => {
const storage = new Storage({ area: "local" })
const result: any = await storage.get("webhistory")
const storage = new Storage({ area: "local" });
const result: any = await storage.get("webhistory");
if (result === undefined) {
await storage.set("webhistory", { webhistory: emptyArr })
return
await storage.set("webhistory", { webhistory: emptyArr });
return;
}
const ifIdExists = result.webhistory.find(
(data: WebHistory) => data.tabsessionId === tabId
)
const ifIdExists = result.webhistory.find((data: WebHistory) => data.tabsessionId === tabId);
if (ifIdExists === undefined) {
let webHistory = result.webhistory
const webHistory = result.webhistory;
const initData = {
tabsessionId: tabId,
tabHistory: emptyArr
}
tabHistory: emptyArr,
};
webHistory.push(initData)
webHistory.push(initData);
try {
await storage.set("webhistory", { webhistory: webHistory })
return
await storage.set("webhistory", { webhistory: webHistory });
return;
} catch (error) {
console.log(error)
console.log(error);
}
} else {
return
}
return;
}
};
export function toIsoString(date: Date) {
var tzo = -date.getTimezoneOffset(),
dif = tzo >= 0 ? "+" : "-",
pad = function (num: number) {
return (num < 10 ? "0" : "") + num
}
pad = (num: number) => (num < 10 ? "0" : "") + num;
return (
date.getFullYear() +
@ -116,14 +112,11 @@ export function toIsoString(date: Date) {
pad(Math.floor(Math.abs(tzo) / 60)) +
":" +
pad(Math.abs(tzo) % 60)
)
);
}
export const webhistoryToLangChainDocument = (
tabId: number,
tabHistory: any[]
) => {
let toSaveFinally = []
export const webhistoryToLangChainDocument = (tabId: number, tabHistory: any[]) => {
const toSaveFinally = [];
for (let j = 0; j < tabHistory.length; j++) {
const mtadata = {
BrowsingSessionId: `${tabId}`,
@ -131,14 +124,14 @@ export const webhistoryToLangChainDocument = (
VisitedWebPageTitle: `${tabHistory[j].title}`,
VisitedWebPageDateWithTimeInISOString: `${toIsoString(new Date(tabHistory[j].entryTime))}`,
VisitedWebPageReffererURL: `${tabHistory[j].reffererUrl}`,
VisitedWebPageVisitDurationInMilliseconds: tabHistory[j].duration
}
VisitedWebPageVisitDurationInMilliseconds: tabHistory[j].duration,
};
toSaveFinally.push({
metadata: mtadata,
pageContent: tabHistory[j].pageContentMarkdown
})
pageContent: tabHistory[j].pageContentMarkdown,
});
}
return toSaveFinally
}
return toSaveFinally;
};