Merge pull request #2 from maglore9900/master

added chrome capability
This commit is contained in:
TheCookingSenpai 2024-10-15 21:47:12 +02:00 committed by GitHub
commit 9f98d7a5cf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 178 additions and 64 deletions

View File

@ -1,11 +1,66 @@
console.log("Background script loaded");
browser.browserAction.onClicked.addListener(() => {
browser.sidebarAction.toggle();
});
let isFirefox = typeof InstallTrigger !== 'undefined'; // Firefox has `InstallTrigger`
let browser = isFirefox ? window.browser : chrome;
// Check if chrome.action or browser.action is available
if (isFirefox && browser.browserAction) {
// Firefox specific: Use browserAction
browser.browserAction.onClicked.addListener(() => {
console.log("Firefox: Toggling sidebar");
browser.sidebarAction.toggle();
});
} else if (browser.action) {
// Chrome specific: Use action and inject the sidebar iframe
browser.action.onClicked.addListener((tab) => {
console.log("Injecting sidebar iframe into the page");
// Use the tab object properly here
browser.scripting.executeScript({
target: { tabId: tab.id }, // Pass the tab ID correctly
function: injectSidebar
}, () => {
if (browser.runtime.lastError) {
console.error("Error injecting sidebar:", browser.runtime.lastError.message);
} else {
console.log("Sidebar injected successfully.");
}
});
});
}
// Function to inject the sidebar as an iframe in browsers like Chrome
function injectSidebar() {
// Check if the sidebar iframe is already injected
if (document.getElementById('sidebar-frame')) {
console.log("Sidebar is already injected.");
return;
}
// Create an iframe for the sidebar
const sidebarFrame = document.createElement('iframe');
sidebarFrame.id = 'sidebar-frame'; // Add an ID to prevent multiple injections
sidebarFrame.src = chrome.runtime.getURL('sidebar/sidebar.html'); // Use the sidebar.html
sidebarFrame.style.cssText = `
position: fixed;
top: 0;
left: 0;
width: 300px;
height: 100%;
border: none;
z-index: 9999;
background-color: white;
`;
// Append the sidebar iframe to the body of the active webpage
document.body.appendChild(sidebarFrame);
}
// Background script listens for the 'summarize' action
browser.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.action === "summarize") {
console.log("Summarization request received in background script.");
const tokenCount = estimateTokenCount(request.content);
summarizeContent(request.content, request.systemPrompt)
.then((summary) => {
@ -19,7 +74,7 @@ browser.runtime.onMessage.addListener((request, sender, sendResponse) => {
tokenCount,
});
});
return true; // Indicates that we will send a response asynchronously
return true; // Indicates that we will send a response asynchronously
}
});

View File

@ -1,3 +1,6 @@
let browser = (typeof chrome !== 'undefined') ? chrome : (typeof browser !== 'undefined') ? browser : null;
function getPageContent() {
console.log("getPageContent called");
return document.body.innerText;

30
manifest_ch.json Normal file
View File

@ -0,0 +1,30 @@
{
"manifest_version": 3,
"name": "SpaceLLama",
"version": "1.21",
"description": "Summarize web pages using Ollama. Supports custom models, token limits, system prompts, chunking, and more. See https://github.com/tcsenpai/spacellama for more information.",
"permissions": ["activeTab", "storage", "tabs", "scripting"],
"action": {
"default_title": "SpaceLLama",
"default_icon": "icon.png"
},
"background": {
"service_worker": "background.js"
},
"content_scripts": [
{
"matches": ["http://*/*", "https://*/*"],
"js": ["content_scripts/content.js"]
}
],
"options_ui": {
"page": "options/options.html",
"open_in_tab": true
},
"web_accessible_resources": [
{
"resources": ["sidebar/sidebar.html", "sidebar/sidebar.js", "sidebar/sidebar.css", "sidebar/marked.min.js", "model_tokens.json"],
"matches": ["http://*/*", "https://*/*"]
}
]
}

View File

@ -1,3 +1,5 @@
let browser = (typeof chrome !== 'undefined') ? chrome : (typeof browser !== 'undefined') ? browser : null;
async function validateEndpoint(endpoint) {
try {
const response = await fetch(`${endpoint}/api/tags`);
@ -14,6 +16,31 @@ function updateEndpointStatus(isValid) {
statusElement.title = isValid ? "Endpoint is valid" : "Endpoint is invalid";
}
async function updateTokenLimit() {
try {
const modelTokens = await loadModelTokens();
const model = document.getElementById("model").value;
const tokenLimitInput = document.getElementById("token-limit");
if (model in modelTokens) {
tokenLimitInput.value = modelTokens[model];
} else {
tokenLimitInput.value = 4000; // Default value, modified from 4096 to meet even requirement
}
} catch (error) {
console.error("Error updating token limit:", error.message || error);
}
}
async function loadModelTokens() {
try {
const response = await fetch(browser.runtime.getURL('model_tokens.json'));
return await response.json();
} catch (error) {
console.error("Error loading model tokens:", error.message || error);
}
}
async function saveOptions(e) {
e.preventDefault();
const endpoint = document.getElementById("endpoint").value;
@ -24,71 +51,58 @@ async function saveOptions(e) {
// Ensure the endpoint doesn't end with /api/generate
const cleanEndpoint = endpoint.replace(/\/api\/generate\/?$/, "");
status.textContent = "Validating endpoint...";
const isValid = await validateEndpoint(cleanEndpoint);
updateEndpointStatus(isValid);
if (isValid) {
browser.storage.local
.set({
try {
const isValid = await validateEndpoint(cleanEndpoint);
updateEndpointStatus(isValid);
if (isValid) {
await browser.storage.local.set({
ollamaEndpoint: cleanEndpoint,
ollamaModel: model,
systemPrompt: systemPrompt,
tokenLimit: parseInt(tokenLimit),
})
.then(() => {
status.textContent = "Options saved and endpoint validated.";
setTimeout(() => {
status.textContent = "";
}, 2000);
});
} else {
status.textContent =
status.textContent = "Options saved and endpoint validated.";
setTimeout(() => {
status.textContent = "";
}, 2000);
} else {
status.textContent =
"Invalid endpoint. Please check the URL and try again.";
}
} catch (error) {
console.error("Error saving options:", error.message || error);
status.textContent = "Error saving options.";
}
}
async function restoreOptions() {
const result = await browser.storage.local.get([
"ollamaEndpoint",
"ollamaModel",
"systemPrompt",
"tokenLimit",
]);
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;
await updateTokenLimit();
const isValid = await validateEndpoint(endpoint);
updateEndpointStatus(isValid);
function restoreOptions() {
browser.storage.local.get({
ollamaEndpoint: "http://localhost:11434",
ollamaModel: "llama2",
systemPrompt: "You are a helpful AI assistant. Summarize the given text concisely.",
tokenLimit: 4096
}, function(result) {
document.getElementById("endpoint").value = result.ollamaEndpoint || "http://localhost:11434";
document.getElementById("model").value = result.ollamaModel || "llama2";
document.getElementById("system-prompt").value = result.systemPrompt || "You are a helpful AI assistant. Summarize the given text concisely.";
// Call to updateTokenLimit remains async
updateTokenLimit().then(() => {
validateEndpoint(result.ollamaEndpoint).then(isValid => {
updateEndpointStatus(isValid);
});
});
});
}
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);
});
async function loadModelTokens() {
const response = await fetch(browser.runtime.getURL('model_tokens.json'));
return await response.json();
}
async function updateTokenLimit() {
const modelTokens = await loadModelTokens();
const model = document.getElementById("model").value;
const tokenLimitInput = document.getElementById("token-limit");
if (model in modelTokens) {
tokenLimitInput.value = modelTokens[model];
} else {
tokenLimitInput.value = 4096; // Default value
}
}
const isValid = await validateEndpoint(e.target.value);
updateEndpointStatus(isValid);
document.getElementById("model").addEventListener("change", updateTokenLimit);
});

View File

@ -47,20 +47,21 @@ h1 {
}
.summary-container {
margin-top: 20px;
padding: 15px;
margin-top: 10px;
padding: 10px;
background-color: white;
border-radius: 5px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
}
#summary h1, #summary h2, #summary h3 {
margin-top: 15px;
margin-bottom: 10px;
margin-top: 0px;
margin-bottom: 0px;
color: #2c3e50;
}
#summary p {
margin-top: 10px;
margin-bottom: 10px;
line-height: 1.6;
}
@ -68,6 +69,12 @@ h1 {
#summary ul, #summary ol {
padding-left: 20px;
margin-bottom: 10px;
margin-top: 10px;
}
#summary li {
margin-bottom: 10px;
}
#summary code {
@ -90,7 +97,7 @@ h1 {
}
.form-group {
margin-top: 20px;
margin-top: 10px;
}
label {
@ -120,6 +127,6 @@ textarea {
}
#summary {
white-space: pre-wrap;
/* white-space: pre-wrap; */
word-wrap: break-word;
}

View File

@ -1,3 +1,6 @@
let isFirefox = typeof InstallTrigger !== 'undefined'; // Firefox has `InstallTrigger`
let browser = isFirefox ? window.browser : chrome;
document.addEventListener("DOMContentLoaded", () => {
const summarizeButton = document.getElementById("summarize");
const summaryDiv = document.getElementById("summary");
@ -8,12 +11,15 @@ document.addEventListener("DOMContentLoaded", () => {
tokenCountDiv.style.fontStyle = "italic";
summarizeButton.parentNode.insertBefore(tokenCountDiv, summarizeButton.nextSibling);
// Correctly define systemPromptTextarea
const systemPromptTextarea = document.getElementById("system-prompt");
summarizeButton.addEventListener("click", () => {
summaryDiv.innerHTML = "<p>Summarizing...</p>";
tokenCountDiv.textContent = "";
summarizeButton.disabled = true;
// Get the current tab content
browser.tabs.query({ active: true, currentWindow: true }, (tabs) => {
browser.tabs.sendMessage(
tabs[0].id,
@ -28,6 +34,7 @@ document.addEventListener("DOMContentLoaded", () => {
if (response && response.content) {
const systemPrompt = systemPromptTextarea.value;
// Send message to background script for summarization
browser.runtime.sendMessage(
{ action: "summarize", content: response.content, systemPrompt: systemPrompt },
(response) => {
@ -50,7 +57,6 @@ document.addEventListener("DOMContentLoaded", () => {
</div>
`;
}
let summaryText;
if (typeof response.summary === 'string') {
summaryText = response.summary;
@ -59,7 +65,7 @@ document.addEventListener("DOMContentLoaded", () => {
summaryText = Object.entries(response.summary)
.map(([key, value]) => `## ${key}\n\n${value}`)
.join('\n\n');
} else {
} else {
summaryText = JSON.stringify(response.summary);
}
@ -98,7 +104,6 @@ document.addEventListener("DOMContentLoaded", () => {
summarizeButton.disabled = false;
}
});
const systemPromptTextarea = document.getElementById("system-prompt");
const savePromptButton = document.getElementById("save-prompt");
@ -113,4 +118,4 @@ savePromptButton.addEventListener("click", () => {
browser.storage.local.set({ systemPrompt }).then(() => {
alert("System prompt saved successfully!");
});
});
});