diff --git a/background.js b/background.js index df189ae..ccd4295 100644 --- a/background.js +++ b/background.js @@ -6,50 +6,58 @@ browser.browserAction.onClicked.addListener(() => { browser.runtime.onMessage.addListener((request, sender, sendResponse) => { if (request.action === "summarize") { - summarizeContent(request.content) - .then(summary => { + summarizeContent(request.content, request.systemPrompt) + .then((summary) => { sendResponse({ summary }); }) - .catch(error => { - console.error('Error in summarizeContent:', error); + .catch((error) => { + console.error("Error in summarizeContent:", error); sendResponse({ error: error.toString(), details: error.details }); }); return true; // Indicates that we will send a response asynchronously } }); -async function summarizeContent(content) { - const settings = await browser.storage.local.get(['ollamaEndpoint', 'ollamaModel']); - const endpoint = `${settings.ollamaEndpoint || 'http://localhost:11434'}/api/generate`; - const model = settings.ollamaModel || 'llama2'; +async function summarizeContent(content, systemPrompt) { + const settings = await browser.storage.local.get([ + "ollamaEndpoint", + "ollamaModel", + ]); + const endpoint = `${ + settings.ollamaEndpoint || "http://localhost:11434" + }/api/generate`; + const model = settings.ollamaModel || "llama3.1:8b"; try { + console.log(`Using system prompt: ${systemPrompt}`); const response = await fetch(endpoint, { - method: 'POST', + method: "POST", headers: { - 'Content-Type': 'application/json', + "Content-Type": "application/json", }, body: JSON.stringify({ - prompt: `Summarize the following text:\n\n${content}`, + prompt: `${systemPrompt}\n\nFollow the above instructions and summarize the following text:\n\n${content}`, model: model, - stream: false + stream: false, }), }); if (!response.ok) { const errorText = await response.text(); - throw new Error(`HTTP error! status: ${response.status}, message: ${errorText}`); + throw new Error( + `HTTP error! status: ${response.status}, message: ${errorText}` + ); } const data = await response.json(); return data.response; } catch (error) { - console.error('Error details:', error); + console.error("Error details:", error); error.details = { endpoint: endpoint, model: model, - message: error.message + message: error.message, }; throw error; } -} \ No newline at end of file +} diff --git a/options/options.css b/options/options.css index f696dd9..b733068 100644 --- a/options/options.css +++ b/options/options.css @@ -1,90 +1,104 @@ body { - font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; - margin: 0; - padding: 0; - background-color: #f5f5f5; - color: #333; + font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; + margin: 0; + padding: 0; + background-color: #f5f5f5; + color: #333; } .container { - max-width: 600px; - margin: 0 auto; - padding: 40px 20px; + max-width: 600px; + margin: 0 auto; + padding: 40px 20px; } h1 { - font-size: 28px; - margin-bottom: 30px; - color: #2c3e50; + font-size: 28px; + margin-bottom: 30px; + color: #2c3e50; } .form-group { - margin-bottom: 20px; + margin-bottom: 20px; } label { - display: block; - margin-bottom: 5px; - font-weight: bold; - color: #34495e; + display: block; + margin-bottom: 5px; + font-weight: bold; + color: #34495e; } .input-group { - display: flex; - align-items: center; + display: flex; + align-items: center; } input[type="text"] { - width: 100%; - padding: 10px; - font-size: 16px; - border: 1px solid #bdc3c7; - border-radius: 5px; - transition: border-color 0.3s ease; + width: 100%; + padding: 10px; + font-size: 16px; + border: 1px solid #bdc3c7; + border-radius: 5px; + transition: border-color 0.3s ease; } input[type="text"]:focus { - outline: none; - border-color: #3498db; + outline: none; + border-color: #3498db; } .status-indicator { - margin-left: 10px; - font-size: 20px; + margin-left: 10px; + font-size: 20px; } .btn { - display: inline-block; - padding: 10px 20px; - font-size: 16px; - border: none; - border-radius: 5px; - cursor: pointer; - transition: background-color 0.3s ease; + display: inline-block; + padding: 10px 20px; + font-size: 16px; + border: none; + border-radius: 5px; + cursor: pointer; + transition: background-color 0.3s ease; } .btn-primary { - background-color: #3498db; - color: white; + background-color: #3498db; + color: white; } .btn-primary:hover { - background-color: #2980b9; + background-color: #2980b9; } .status-message { - margin-top: 20px; - padding: 10px; - border-radius: 5px; - font-weight: bold; + margin-top: 20px; + padding: 10px; + border-radius: 5px; + font-weight: bold; } .status-message.success { - background-color: #2ecc71; - color: white; + background-color: #2ecc71; + color: white; } .status-message.error { - background-color: #e74c3c; - color: white; -} \ No newline at end of file + background-color: #e74c3c; + color: white; +} + +textarea { + width: 100%; + padding: 10px; + font-size: 16px; + border: 1px solid #bdc3c7; + border-radius: 5px; + transition: border-color 0.3s ease; + resize: vertical; +} +textarea:focus { + outline: none; + border-color: #3498db; +} diff --git a/options/options.html b/options/options.html index 270d370..2ed91ec 100644 --- a/options/options.html +++ b/options/options.html @@ -1,30 +1,38 @@ - - - - OLLAMA Summarizer Settings - - - -
-

OLLAMA Summarizer Settings

-
-
- -
- - + + + + OLLAMA Summarizer Settings + + + +
+

OLLAMA Summarizer Settings

+ +
+ +
+ + +
-
-
- - -
- - -
-
- - - \ No newline at end of file +
+ + +
+ +
+ + +
+ +
+
+ + + diff --git a/options/options.js b/options/options.js index faf272d..73cfc73 100644 --- a/options/options.js +++ b/options/options.js @@ -3,58 +3,67 @@ async function validateEndpoint(endpoint) { const response = await fetch(`${endpoint}/api/tags`); return response.ok; } catch (error) { - console.error('Error validating endpoint:', error); + console.error("Error validating endpoint:", error); return false; } } function updateEndpointStatus(isValid) { - const statusElement = document.getElementById('endpoint-status'); - statusElement.textContent = isValid ? '✅' : '❌'; - statusElement.title = isValid ? 'Endpoint is valid' : 'Endpoint is invalid'; + const statusElement = document.getElementById("endpoint-status"); + statusElement.textContent = isValid ? "✅" : "❌"; + statusElement.title = isValid ? "Endpoint is valid" : "Endpoint is invalid"; } async function saveOptions(e) { e.preventDefault(); - const endpoint = document.getElementById('endpoint').value; - const model = document.getElementById('model').value; - const status = document.getElementById('status'); - + const endpoint = document.getElementById("endpoint").value; + const model = document.getElementById("model").value; + const systemPrompt = document.getElementById("system-prompt").value; + const status = document.getElementById("status"); // Ensure the endpoint doesn't end with /api/generate - const cleanEndpoint = endpoint.replace(/\/api\/generate\/?$/, ''); - - status.textContent = 'Validating endpoint...'; + const cleanEndpoint = endpoint.replace(/\/api\/generate\/?$/, ""); + status.textContent = "Validating endpoint..."; const isValid = await validateEndpoint(cleanEndpoint); updateEndpointStatus(isValid); - if (isValid) { - browser.storage.local.set({ - ollamaEndpoint: cleanEndpoint, - ollamaModel: model - }).then(() => { - status.textContent = 'Options saved and endpoint validated.'; - setTimeout(() => { - status.textContent = ''; - }, 2000); - }); + browser.storage.local + .set({ + ollamaEndpoint: cleanEndpoint, + ollamaModel: model, + systemPrompt: systemPrompt, + }) + .then(() => { + status.textContent = "Options saved and endpoint validated."; + setTimeout(() => { + status.textContent = ""; + }, 2000); + }); } else { - status.textContent = 'Invalid endpoint. Please check the URL and try again.'; + status.textContent = + "Invalid endpoint. Please check the URL and try again."; } } async function restoreOptions() { - const result = await browser.storage.local.get(['ollamaEndpoint', 'ollamaModel']); - const endpoint = result.ollamaEndpoint || 'http://localhost:11434'; - document.getElementById('endpoint').value = endpoint; - document.getElementById('model').value = result.ollamaModel || 'llama2'; - + const result = await browser.storage.local.get([ + "ollamaEndpoint", + "ollamaModel", + "systemPrompt", + ]); + const endpoint = result.ollamaEndpoint || "http://localhost:11434"; + const defaultSystemPrompt = "You are a helpful AI assistant. Summarize the given text concisely."; + document.getElementById("endpoint").value = endpoint; + document.getElementById("model").value = result.ollamaModel || "llama2"; + document.getElementById("system-prompt").value = result.systemPrompt || defaultSystemPrompt; const isValid = await validateEndpoint(endpoint); updateEndpointStatus(isValid); } -document.addEventListener('DOMContentLoaded', restoreOptions); -document.getElementById('settings-form').addEventListener('submit', saveOptions); -document.getElementById('endpoint').addEventListener('blur', async (e) => { +document.addEventListener("DOMContentLoaded", restoreOptions); +document + .getElementById("settings-form") + .addEventListener("submit", saveOptions); +document.getElementById("endpoint").addEventListener("blur", async (e) => { const isValid = await validateEndpoint(e.target.value); updateEndpointStatus(isValid); -}); \ No newline at end of file +}); diff --git a/sidebar/sidebar.css b/sidebar/sidebar.css index a2b067d..3c217ca 100644 --- a/sidebar/sidebar.css +++ b/sidebar/sidebar.css @@ -87,4 +87,39 @@ h1 { #open-options { margin-top: 20px; -} \ No newline at end of file +} + +.form-group { + margin-top: 20px; +} + +label { + display: block; + margin-bottom: 5px; + font-weight: bold; +} + +textarea { + width: 100%; + padding: 10px; + border: 1px solid #ccc; + border-radius: 4px; + resize: vertical; +} + +#save-prompt { + margin-top: 10px; + margin-bottom: 10px; + } + +#summary, #system-prompt { + user-select: text; + -webkit-user-select: text; + -moz-user-select: text; + -ms-user-select: text; +} + +#summary { + white-space: pre-wrap; + word-wrap: break-word; +} diff --git a/sidebar/sidebar.html b/sidebar/sidebar.html index 2fde5bf..6a0c592 100644 --- a/sidebar/sidebar.html +++ b/sidebar/sidebar.html @@ -11,8 +11,13 @@

OLLAMA Summarizer

+
+ + +
+
- \ No newline at end of file + diff --git a/sidebar/sidebar.js b/sidebar/sidebar.js index 1738144..cb605ab 100644 --- a/sidebar/sidebar.js +++ b/sidebar/sidebar.js @@ -1,28 +1,37 @@ -document.addEventListener('DOMContentLoaded', () => { - const summarizeButton = document.getElementById('summarize'); - const summaryDiv = document.getElementById('summary'); - const openOptionsButton = document.getElementById('open-options'); - - summarizeButton.addEventListener('click', () => { - summaryDiv.innerHTML = '

Summarizing...

'; - summarizeButton.disabled = true; - - browser.tabs.query({ active: true, currentWindow: true }, (tabs) => { - browser.tabs.sendMessage(tabs[0].id, { action: "getContent" }, (response) => { +document.addEventListener("DOMContentLoaded", () => { + const summarizeButton = document.getElementById("summarize"); + const summaryDiv = document.getElementById("summary"); + const openOptionsButton = document.getElementById("open-options"); + + summarizeButton.addEventListener("click", () => { + summaryDiv.innerHTML = "

Summarizing...

"; + summarizeButton.disabled = true; + + browser.tabs.query({ active: true, currentWindow: true }, (tabs) => { + browser.tabs.sendMessage( + tabs[0].id, + { action: "getContent" }, + (response) => { if (browser.runtime.lastError) { - handleError('Error getting page content: ' + browser.runtime.lastError.message); + handleError( + "Error getting page content: " + browser.runtime.lastError.message + ); return; } - + if (response && response.content) { + const systemPrompt = systemPromptTextarea.value; browser.runtime.sendMessage( - { action: "summarize", content: response.content }, + { action: "summarize", content: response.content, systemPrompt: systemPrompt }, (response) => { if (browser.runtime.lastError) { - handleError('Error during summarization: ' + browser.runtime.lastError.message); + handleError( + "Error during summarization: " + + browser.runtime.lastError.message + ); return; } - + if (response && response.summary) { // Render the Markdown content summaryDiv.innerHTML = marked.parse(response.summary); @@ -35,22 +44,39 @@ document.addEventListener('DOMContentLoaded', () => { } ); } else { - handleError('Error: Could not retrieve page content.'); + handleError("Error: Could not retrieve page content."); } - }); - }); + } + ); }); - - openOptionsButton.addEventListener('click', () => { - browser.runtime.openOptionsPage(); - }); - - function handleError(errorMessage, details = null) { - console.error("Error:", errorMessage, details); - summaryDiv.innerHTML = `

Error: ${errorMessage}

`; - if (details) { - summaryDiv.innerHTML += `
${JSON.stringify(details, null, 2)}
`; - } - summarizeButton.disabled = false; + }); + + openOptionsButton.addEventListener("click", () => { + browser.runtime.openOptionsPage(); + }); + + function handleError(errorMessage, details = null) { + console.error("Error:", errorMessage, details); + summaryDiv.innerHTML = `

Error: ${errorMessage}

`; + if (details) { + summaryDiv.innerHTML += `
${JSON.stringify(details, null, 2)}
`; } - }); \ No newline at end of file + summarizeButton.disabled = false; + } +}); + +const systemPromptTextarea = document.getElementById("system-prompt"); +const savePromptButton = document.getElementById("save-prompt"); + +// Load saved system prompt +browser.storage.local.get("systemPrompt").then((result) => { + const defaultSystemPrompt = "You are a helpful AI assistant. Summarize the given text concisely."; + systemPromptTextarea.value = result.systemPrompt || defaultSystemPrompt; +}); + +savePromptButton.addEventListener("click", () => { + const systemPrompt = systemPromptTextarea.value; + browser.storage.local.set({ systemPrompt }).then(() => { + alert("System prompt saved successfully!"); + }); +});