diff --git a/chrome-extension/package.json b/chrome-extension/package.json index 126e4c6..ff03b28 100644 --- a/chrome-extension/package.json +++ b/chrome-extension/package.json @@ -14,6 +14,7 @@ "author": "", "license": "ISC", "dependencies": { + "html-to-markdown-wasm": "^2.5.6", "ollama": "^0.5.16" }, "devDependencies": { diff --git a/chrome-extension/pnpm-lock.yaml b/chrome-extension/pnpm-lock.yaml index 68bbda6..feb0466 100644 --- a/chrome-extension/pnpm-lock.yaml +++ b/chrome-extension/pnpm-lock.yaml @@ -8,6 +8,9 @@ importers: .: dependencies: + html-to-markdown-wasm: + specifier: ^2.5.6 + version: 2.5.6 ollama: specifier: ^0.5.16 version: 0.5.16 @@ -265,6 +268,9 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} + html-to-markdown-wasm@2.5.6: + resolution: {integrity: sha512-YQxvcN5KjrZAinKxI26p66f3sP+j6mG6SCF3JhwzvnicVPfC6v7rYMMlAa7BuyhemIywKO2IlAyuzWwCS2VOZA==} + import-local@3.2.0: resolution: {integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==} engines: {node: '>=8'} @@ -450,6 +456,9 @@ packages: undici-types@6.20.0: resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} + up@1.0.2: + resolution: {integrity: sha512-PKVGUj7i1qMWSR83H3PZpw1h+21wAr1qxqF/8zgTVHS+sHTsu1zvepQDoDJN0Kv49quNJwWgAKDAK2xPy7jocg==} + update-browserslist-db@1.1.2: resolution: {integrity: sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==} hasBin: true @@ -744,6 +753,10 @@ snapshots: dependencies: function-bind: 1.1.2 + html-to-markdown-wasm@2.5.6: + dependencies: + up: 1.0.2 + import-local@3.2.0: dependencies: pkg-dir: 4.2.0 @@ -897,6 +910,8 @@ snapshots: undici-types@6.20.0: {} + up@1.0.2: {} + update-browserslist-db@1.1.2(browserslist@4.24.4): dependencies: browserslist: 4.24.4 diff --git a/chrome-extension/src/content.js b/chrome-extension/src/content.js index 13caa93..ba6c7ab 100644 --- a/chrome-extension/src/content.js +++ b/chrome-extension/src/content.js @@ -1,22 +1,52 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { - if (request.action === "getPageInfo") { - const pageInfo = { - url: window.location.href, - title: document.title, - description: getDescription(), - }; - sendResponse(pageInfo); - } + if (request.action === "getPageInfo") { + const pageInfo = { + url: window.location.href, + title: document.title, + description: getDescription(), + }; + sendResponse(pageInfo); + } + if (request.action === "getBodyContent") { + sendResponse(getContent()); + } }); function getDescription() { - // Try meta description first - const metaDesc = - document.querySelector('meta[property="og:description"]')?.content || - document.querySelector('meta[name="description"]')?.content; - if (metaDesc) return metaDesc; + // Try meta description first + const metaDesc = + document.querySelector('meta[property="og:description"]')?.content || + document.querySelector('meta[name="description"]')?.content; + if (metaDesc) return metaDesc; - // Fallback to first paragraph - const firstPara = document.querySelector("p")?.textContent; - return firstPara ? firstPara.substring(0, 200) + "..." : ""; + // Fallback to first paragraph + const firstPara = document.querySelector("p")?.textContent; + return firstPara ? firstPara.substring(0, 200) + "..." : ""; +} + +function getContent() { + // Highest priority query to lowest priority + let queries = ["article", "#content", "main", ".main", "body"]; + for (let i = 0; i < queries.length; i++) { + let root = document.querySelector(queries[i]); + if (root != null) { + return getTextContent(root); + } + } + return "" +} + + +function getTextContent(root) { + let walker = document.createTreeWalker(root, + NodeFilter.SHOW_TEXT, + null + ); + + let node; + let textContent = "" + while (node = walker.nextNode()) { + textContent += node.nodeValue + " "; + } + return textContent; } diff --git a/chrome-extension/src/default_prompts.js b/chrome-extension/src/default_prompts.js new file mode 100644 index 0000000..131e118 --- /dev/null +++ b/chrome-extension/src/default_prompts.js @@ -0,0 +1,13 @@ +export let tagging_prompt = `Generate 3-5 relevant tags for this content. Return only the tags as a JSON array of strings. No additional explanation needed. + +Title: {{title}} +URL: {{url}} +Description: {{description}}` +export let description_prompt = `URL: {{url}} +Content: +{{content}} + +Title: +{{title}} + +Please write a short google search style description for the following webpage. Avoid describing the overall website and focus on the content on the page. Avoid phrases like 'news article about'. Return only the description as in the json object as a string. No additional explanation needed.`; diff --git a/chrome-extension/src/description_generation.js b/chrome-extension/src/description_generation.js new file mode 100644 index 0000000..d0f2177 --- /dev/null +++ b/chrome-extension/src/description_generation.js @@ -0,0 +1,60 @@ +import { Ollama } from "ollama/browser"; +import { description_schema } from "./schema" + +async function generateDescription(title, url, content) { + try { + // Get Ollama settings + const { + ollamaEndpoint, + ollamaSummaryModel: ollamaModel, + ollamaSummaryPrompt: ollamaPrompt, + ollamaSummaryTemperature: temperature, + } = await chrome.storage.sync.get([ + "ollamaEndpoint", + "ollamaSummaryModel", + "ollamaSummaryPrompt", + "ollamaSummaryTemperature", + ]); + console.log({ ollamaEndpoint, ollamaModel, ollamaPrompt, temperature }) + + const endpoint = ollamaEndpoint || "http://localhost:11434"; + const model = ollamaModel || "gemma3:4b"; + + // Use custom prompt if available, otherwise use default + let promptTemplate = ollamaPrompt || + `Please generate a short and simple description of the the following document in the style of a website description. +Document: {{content}}`; + + // Replace placeholders with actual content + const prompt = promptTemplate + .replace(/{{title}}/g, title) + .replace(/{{url}}/g, url) + .replace(/{{content}}/g, content); + + // Initialize Ollama client + const ollamaClient = new Ollama({ + host: endpoint, + }); + + const response = await ollamaClient.generate({ + model: model, + prompt: prompt, + options: { + temperature: temperature || 0.3, + }, + format: description_schema, + }); + + let desc = JSON.parse(response.response).description; + + return desc; + } catch (error) { + console.error("Error generating description with Ollama:", error); + updateStatus(`Error generating description: ${error.message}`); + return []; + } +} + +export { + generateDescription, +} diff --git a/chrome-extension/src/options.html b/chrome-extension/src/options.html index bf74fa2..d47f93d 100644 --- a/chrome-extension/src/options.html +++ b/chrome-extension/src/options.html @@ -1,139 +1,154 @@ - + -
-