mirror of
https://github.com/tcsenpai/spacellama.git
synced 2025-07-24 02:20:10 +00:00
commit
9f98d7a5cf
@ -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
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -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
30
manifest_ch.json
Normal 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://*/*"]
|
||||
}
|
||||
]
|
||||
}
|
@ -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);
|
||||
});
|
@ -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;
|
||||
}
|
||||
|
@ -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!");
|
||||
});
|
||||
});
|
||||
});
|
Loading…
x
Reference in New Issue
Block a user