From 72aa542080602c87f4fd7640ccc9080252aa9bf0 Mon Sep 17 00:00:00 2001 From: tcsenpai Date: Fri, 11 Oct 2024 13:28:02 +0200 Subject: [PATCH] first commit --- .gitignore | 3 ++ LICENSE.md | 14 ++++++ README.md | 56 +++++++++++++++++++++++ background.js | 55 +++++++++++++++++++++++ content_scripts/content.js | 16 +++++++ icon.png | Bin 0 -> 40771 bytes manifest.json | 38 ++++++++++++++++ options/options.css | 90 +++++++++++++++++++++++++++++++++++++ options/options.html | 30 +++++++++++++ options/options.js | 60 +++++++++++++++++++++++++ sidebar/marked.min.js | 6 +++ sidebar/sidebar.css | 90 +++++++++++++++++++++++++++++++++++++ sidebar/sidebar.html | 18 ++++++++ sidebar/sidebar.js | 56 +++++++++++++++++++++++ 14 files changed, 532 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE.md create mode 100644 README.md create mode 100644 background.js create mode 100644 content_scripts/content.js create mode 100644 icon.png create mode 100644 manifest.json create mode 100644 options/options.css create mode 100644 options/options.html create mode 100644 options/options.js create mode 100644 sidebar/marked.min.js create mode 100644 sidebar/sidebar.css create mode 100644 sidebar/sidebar.html create mode 100644 sidebar/sidebar.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c6e6892 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +extension +pack_extension.sh +SpaceLLama.zip \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..82e71ee --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,14 @@ + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + Version 2, December 2004 + +Copyright (C) 2024 tcsenpai + +Everyone is permitted to copy and distribute verbatim or modified +copies of this license document, and changing it is allowed as long +as the name is changed. + + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. You just DO WHAT THE FUCK YOU WANT TO. diff --git a/README.md b/README.md new file mode 100644 index 0000000..46d10ce --- /dev/null +++ b/README.md @@ -0,0 +1,56 @@ +# SpaceLLama + +SpaceLLama is a powerful browser extension that leverages OLLAMA to provide quick and efficient web page summarization. It offers a seamless way to distill the essence of any web content, saving you time and enhancing your browsing experience. + +## Features + +- **One-Click Summarization**: Quickly summarize any web page with a single click. +- **Sidebar Integration**: View summaries in a convenient sidebar without leaving the current page. +- **Customizable OLLAMA Settings**: Easily configure the OLLAMA endpoint and model through the options page. +- **Markdown Rendering**: Summaries are rendered in Markdown for better readability and formatting. +- **Error Handling**: Robust error handling with informative messages for troubleshooting. + +## How It Works + +1. Click the SpaceLLama icon in your browser toolbar to open the sidebar. +2. Navigate to the web page you want to summarize. +3. Click the "Summarize" button in the sidebar. +4. Wait for a few seconds as SpaceLLama processes the page content. +5. Read the concise summary presented in the sidebar. + +## Configuration + +You can customize SpaceLLama's behavior through the options page: + +1. Click the "Open Settings" button in the sidebar. +2. Set your preferred OLLAMA endpoint (default is `http://localhost:11434`). +3. Choose the OLLAMA model you want to use (default is `llama2`). +4. Save your settings. + +## Technical Details + +SpaceLLama is built using standard web technologies and the WebExtensions API. It consists of: + +- A background script for handling API requests to OLLAMA. +- A content script for extracting page content. +- A sidebar interface for user interaction and displaying summaries. +- An options page for customizing settings. + +The extension uses the `marked` library to render Markdown content in the summary view. + +## Privacy and Security + +SpaceLLama processes web page content locally through your configured OLLAMA endpoint. No data is sent to external servers beyond what you configure. Always ensure you're using a trusted OLLAMA setup, especially if using a remote endpoint. + +## Contributing + +Contributions to SpaceLLama are welcome! Please feel free to submit issues, feature requests, or pull requests to help improve the extension. + +## License + +Licensed under the [Do What The Fuck You Want To Public License](LICENSE.md). +See [LICENSE.md](LICENSE.md) for more details. + +--- + +SpaceLLama: Bringing the power of OLLAMA to your browser for effortless web page summarization. diff --git a/background.js b/background.js new file mode 100644 index 0000000..df189ae --- /dev/null +++ b/background.js @@ -0,0 +1,55 @@ +console.log("Background script loaded"); + +browser.browserAction.onClicked.addListener(() => { + browser.sidebarAction.toggle(); +}); + +browser.runtime.onMessage.addListener((request, sender, sendResponse) => { + if (request.action === "summarize") { + summarizeContent(request.content) + .then(summary => { + sendResponse({ summary }); + }) + .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'; + + try { + const response = await fetch(endpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + prompt: `Summarize the following text:\n\n${content}`, + model: model, + stream: false + }), + }); + + if (!response.ok) { + const errorText = await response.text(); + 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); + error.details = { + endpoint: endpoint, + model: model, + message: error.message + }; + throw error; + } +} \ No newline at end of file diff --git a/content_scripts/content.js b/content_scripts/content.js new file mode 100644 index 0000000..f336fee --- /dev/null +++ b/content_scripts/content.js @@ -0,0 +1,16 @@ +function getPageContent() { + console.log("getPageContent called"); + return document.body.innerText; + } + + browser.runtime.onMessage.addListener((request, sender, sendResponse) => { + console.log("Content script received message:", request); + if (request.action === "getContent") { + const content = getPageContent(); + console.log("Sending content (first 100 chars):", content.substring(0, 100)); + sendResponse({ content: content }); + } + return true; // Indicate that we will send a response asynchronously + }); + + console.log("Content script loaded"); \ No newline at end of file diff --git a/icon.png b/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..87b1497fa7b185e7091f11e61f18f05badd774ee GIT binary patch literal 40771 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%0D4DmB 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 z", + "tabs" + ], + "browser_action": { + "default_title": "SpaceLLama", + "default_icon": "icon.png" + }, + "sidebar_action": { + "default_title": "SpaceLLama", + "default_panel": "sidebar/sidebar.html", + "default_icon": "icon.png" + }, + "background": { + "scripts": ["background.js"], + "persistent": false + }, + "content_scripts": [ + { + "matches": [""], + "js": ["content_scripts/content.js"] + } + ], + "options_ui": { + "page": "options/options.html", + "open_in_tab": true + }, + "web_accessible_resources": [ + "sidebar/marked.min.js" + ] + } \ No newline at end of file diff --git a/options/options.css b/options/options.css new file mode 100644 index 0000000..f696dd9 --- /dev/null +++ b/options/options.css @@ -0,0 +1,90 @@ +body { + 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; +} + +h1 { + font-size: 28px; + margin-bottom: 30px; + color: #2c3e50; +} + +.form-group { + margin-bottom: 20px; +} + +label { + display: block; + margin-bottom: 5px; + font-weight: bold; + color: #34495e; +} + +.input-group { + 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; +} + +input[type="text"]:focus { + outline: none; + border-color: #3498db; +} + +.status-indicator { + 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; +} + +.btn-primary { + background-color: #3498db; + color: white; +} + +.btn-primary:hover { + background-color: #2980b9; +} + +.status-message { + margin-top: 20px; + padding: 10px; + border-radius: 5px; + font-weight: bold; +} + +.status-message.success { + background-color: #2ecc71; + color: white; +} + +.status-message.error { + background-color: #e74c3c; + color: white; +} \ No newline at end of file diff --git a/options/options.html b/options/options.html new file mode 100644 index 0000000..270d370 --- /dev/null +++ b/options/options.html @@ -0,0 +1,30 @@ + + + + + + OLLAMA Summarizer Settings + + + +
+

OLLAMA Summarizer Settings

+
+
+ +
+ + +
+
+
+ + +
+ +
+
+
+ + + \ No newline at end of file diff --git a/options/options.js b/options/options.js new file mode 100644 index 0000000..faf272d --- /dev/null +++ b/options/options.js @@ -0,0 +1,60 @@ +async function validateEndpoint(endpoint) { + try { + const response = await fetch(`${endpoint}/api/tags`); + return response.ok; + } catch (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'; +} + +async function saveOptions(e) { + e.preventDefault(); + const endpoint = document.getElementById('endpoint').value; + const model = document.getElementById('model').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 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); + }); + } else { + 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 isValid = await validateEndpoint(endpoint); + 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); +}); \ No newline at end of file diff --git a/sidebar/marked.min.js b/sidebar/marked.min.js new file mode 100644 index 0000000..f724cc4 --- /dev/null +++ b/sidebar/marked.min.js @@ -0,0 +1,6 @@ +/** + * marked v14.1.2 - a markdown parser + * Copyright (c) 2011-2024, Christopher Jeffrey. (MIT Licensed) + * https://github.com/markedjs/marked + */ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).marked={})}(this,(function(e){"use strict";function t(){return{async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null}}function n(t){e.defaults=t}e.defaults={async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null};const s=/[&<>"']/,r=new RegExp(s.source,"g"),i=/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/,l=new RegExp(i.source,"g"),o={"&":"&","<":"<",">":">",'"':""","'":"'"},a=e=>o[e];function c(e,t){if(t){if(s.test(e))return e.replace(r,a)}else if(i.test(e))return e.replace(l,a);return e}const h=/(^|[^\[])\^/g;function p(e,t){let n="string"==typeof e?e:e.source;t=t||"";const s={replace:(e,t)=>{let r="string"==typeof t?t:t.source;return r=r.replace(h,"$1"),n=n.replace(e,r),s},getRegex:()=>new RegExp(n,t)};return s}function u(e){try{e=encodeURI(e).replace(/%25/g,"%")}catch{return null}return e}const k={exec:()=>null};function g(e,t){const n=e.replace(/\|/g,((e,t,n)=>{let s=!1,r=t;for(;--r>=0&&"\\"===n[r];)s=!s;return s?"|":" |"})).split(/ \|/);let s=0;if(n[0].trim()||n.shift(),n.length>0&&!n[n.length-1].trim()&&n.pop(),t)if(n.length>t)n.splice(t);else for(;n.length0)return{type:"space",raw:t[0]}}code(e){const t=this.rules.block.code.exec(e);if(t){const e=t[0].replace(/^(?: {1,4}| {0,3}\t)/gm,"");return{type:"code",raw:t[0],codeBlockStyle:"indented",text:this.options.pedantic?e:f(e,"\n")}}}fences(e){const t=this.rules.block.fences.exec(e);if(t){const e=t[0],n=function(e,t){const n=e.match(/^(\s+)(?:```)/);if(null===n)return t;const s=n[1];return t.split("\n").map((e=>{const t=e.match(/^\s+/);if(null===t)return e;const[n]=t;return n.length>=s.length?e.slice(s.length):e})).join("\n")}(e,t[3]||"");return{type:"code",raw:e,lang:t[2]?t[2].trim().replace(this.rules.inline.anyPunctuation,"$1"):t[2],text:n}}}heading(e){const t=this.rules.block.heading.exec(e);if(t){let e=t[2].trim();if(/#$/.test(e)){const t=f(e,"#");this.options.pedantic?e=t.trim():t&&!/ $/.test(t)||(e=t.trim())}return{type:"heading",raw:t[0],depth:t[1].length,text:e,tokens:this.lexer.inline(e)}}}hr(e){const t=this.rules.block.hr.exec(e);if(t)return{type:"hr",raw:f(t[0],"\n")}}blockquote(e){const t=this.rules.block.blockquote.exec(e);if(t){let e=f(t[0],"\n").split("\n"),n="",s="";const r=[];for(;e.length>0;){let t=!1;const i=[];let l;for(l=0;l/.test(e[l]))i.push(e[l]),t=!0;else{if(t)break;i.push(e[l])}e=e.slice(l);const o=i.join("\n"),a=o.replace(/\n {0,3}((?:=+|-+) *)(?=\n|$)/g,"\n $1").replace(/^ {0,3}>[ \t]?/gm,"");n=n?`${n}\n${o}`:o,s=s?`${s}\n${a}`:a;const c=this.lexer.state.top;if(this.lexer.state.top=!0,this.lexer.blockTokens(a,r,!0),this.lexer.state.top=c,0===e.length)break;const h=r[r.length-1];if("code"===h?.type)break;if("blockquote"===h?.type){const t=h,i=t.raw+"\n"+e.join("\n"),l=this.blockquote(i);r[r.length-1]=l,n=n.substring(0,n.length-t.raw.length)+l.raw,s=s.substring(0,s.length-t.text.length)+l.text;break}if("list"!==h?.type);else{const t=h,i=t.raw+"\n"+e.join("\n"),l=this.list(i);r[r.length-1]=l,n=n.substring(0,n.length-h.raw.length)+l.raw,s=s.substring(0,s.length-t.raw.length)+l.raw,e=i.substring(r[r.length-1].raw.length).split("\n")}}return{type:"blockquote",raw:n,tokens:r,text:s}}}list(e){let t=this.rules.block.list.exec(e);if(t){let n=t[1].trim();const s=n.length>1,r={type:"list",raw:"",ordered:s,start:s?+n.slice(0,-1):"",loose:!1,items:[]};n=s?`\\d{1,9}\\${n.slice(-1)}`:`\\${n}`,this.options.pedantic&&(n=s?n:"[*+-]");const i=new RegExp(`^( {0,3}${n})((?:[\t ][^\\n]*)?(?:\\n|$))`);let l=!1;for(;e;){let n=!1,s="",o="";if(!(t=i.exec(e)))break;if(this.rules.block.hr.test(e))break;s=t[0],e=e.substring(s.length);let a=t[2].split("\n",1)[0].replace(/^\t+/,(e=>" ".repeat(3*e.length))),c=e.split("\n",1)[0],h=!a.trim(),p=0;if(this.options.pedantic?(p=2,o=a.trimStart()):h?p=t[1].length+1:(p=t[2].search(/[^ ]/),p=p>4?1:p,o=a.slice(p),p+=t[1].length),h&&/^[ \t]*$/.test(c)&&(s+=c+"\n",e=e.substring(c.length+1),n=!0),!n){const t=new RegExp(`^ {0,${Math.min(3,p-1)}}(?:[*+-]|\\d{1,9}[.)])((?:[ \t][^\\n]*)?(?:\\n|$))`),n=new RegExp(`^ {0,${Math.min(3,p-1)}}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$)`),r=new RegExp(`^ {0,${Math.min(3,p-1)}}(?:\`\`\`|~~~)`),i=new RegExp(`^ {0,${Math.min(3,p-1)}}#`),l=new RegExp(`^ {0,${Math.min(3,p-1)}}<[a-z].*>`,"i");for(;e;){const u=e.split("\n",1)[0];let k;if(c=u,this.options.pedantic?(c=c.replace(/^ {1,4}(?=( {4})*[^ ])/g," "),k=c):k=c.replace(/\t/g," "),r.test(c))break;if(i.test(c))break;if(l.test(c))break;if(t.test(c))break;if(n.test(c))break;if(k.search(/[^ ]/)>=p||!c.trim())o+="\n"+k.slice(p);else{if(h)break;if(a.replace(/\t/g," ").search(/[^ ]/)>=4)break;if(r.test(a))break;if(i.test(a))break;if(n.test(a))break;o+="\n"+c}h||c.trim()||(h=!0),s+=u+"\n",e=e.substring(u.length+1),a=k.slice(p)}}r.loose||(l?r.loose=!0:/\n[ \t]*\n[ \t]*$/.test(s)&&(l=!0));let u,k=null;this.options.gfm&&(k=/^\[[ xX]\] /.exec(o),k&&(u="[ ] "!==k[0],o=o.replace(/^\[[ xX]\] +/,""))),r.items.push({type:"list_item",raw:s,task:!!k,checked:u,loose:!1,text:o,tokens:[]}),r.raw+=s}r.items[r.items.length-1].raw=r.items[r.items.length-1].raw.trimEnd(),r.items[r.items.length-1].text=r.items[r.items.length-1].text.trimEnd(),r.raw=r.raw.trimEnd();for(let e=0;e"space"===e.type)),n=t.length>0&&t.some((e=>/\n.*\n/.test(e.raw)));r.loose=n}if(r.loose)for(let e=0;e$/,"$1").replace(this.rules.inline.anyPunctuation,"$1"):"",s=t[3]?t[3].substring(1,t[3].length-1).replace(this.rules.inline.anyPunctuation,"$1"):t[3];return{type:"def",tag:e,raw:t[0],href:n,title:s}}}table(e){const t=this.rules.block.table.exec(e);if(!t)return;if(!/[:|]/.test(t[2]))return;const n=g(t[1]),s=t[2].replace(/^\||\| *$/g,"").split("|"),r=t[3]&&t[3].trim()?t[3].replace(/\n[ \t]*$/,"").split("\n"):[],i={type:"table",raw:t[0],header:[],align:[],rows:[]};if(n.length===s.length){for(const e of s)/^ *-+: *$/.test(e)?i.align.push("right"):/^ *:-+: *$/.test(e)?i.align.push("center"):/^ *:-+ *$/.test(e)?i.align.push("left"):i.align.push(null);for(let e=0;e({text:e,tokens:this.lexer.inline(e),header:!1,align:i.align[t]}))));return i}}lheading(e){const t=this.rules.block.lheading.exec(e);if(t)return{type:"heading",raw:t[0],depth:"="===t[2].charAt(0)?1:2,text:t[1],tokens:this.lexer.inline(t[1])}}paragraph(e){const t=this.rules.block.paragraph.exec(e);if(t){const e="\n"===t[1].charAt(t[1].length-1)?t[1].slice(0,-1):t[1];return{type:"paragraph",raw:t[0],text:e,tokens:this.lexer.inline(e)}}}text(e){const t=this.rules.block.text.exec(e);if(t)return{type:"text",raw:t[0],text:t[0],tokens:this.lexer.inline(t[0])}}escape(e){const t=this.rules.inline.escape.exec(e);if(t)return{type:"escape",raw:t[0],text:c(t[1])}}tag(e){const t=this.rules.inline.tag.exec(e);if(t)return!this.lexer.state.inLink&&/^/i.test(t[0])&&(this.lexer.state.inLink=!1),!this.lexer.state.inRawBlock&&/^<(pre|code|kbd|script)(\s|>)/i.test(t[0])?this.lexer.state.inRawBlock=!0:this.lexer.state.inRawBlock&&/^<\/(pre|code|kbd|script)(\s|>)/i.test(t[0])&&(this.lexer.state.inRawBlock=!1),{type:"html",raw:t[0],inLink:this.lexer.state.inLink,inRawBlock:this.lexer.state.inRawBlock,block:!1,text:t[0]}}link(e){const t=this.rules.inline.link.exec(e);if(t){const e=t[2].trim();if(!this.options.pedantic&&/^$/.test(e))return;const t=f(e.slice(0,-1),"\\");if((e.length-t.length)%2==0)return}else{const e=function(e,t){if(-1===e.indexOf(t[1]))return-1;let n=0;for(let s=0;s-1){const n=(0===t[0].indexOf("!")?5:4)+t[1].length+e;t[2]=t[2].substring(0,e),t[0]=t[0].substring(0,n).trim(),t[3]=""}}let n=t[2],s="";if(this.options.pedantic){const e=/^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(n);e&&(n=e[1],s=e[3])}else s=t[3]?t[3].slice(1,-1):"";return n=n.trim(),/^$/.test(e)?n.slice(1):n.slice(1,-1)),d(t,{href:n?n.replace(this.rules.inline.anyPunctuation,"$1"):n,title:s?s.replace(this.rules.inline.anyPunctuation,"$1"):s},t[0],this.lexer)}}reflink(e,t){let n;if((n=this.rules.inline.reflink.exec(e))||(n=this.rules.inline.nolink.exec(e))){const e=t[(n[2]||n[1]).replace(/\s+/g," ").toLowerCase()];if(!e){const e=n[0].charAt(0);return{type:"text",raw:e,text:e}}return d(n,e,n[0],this.lexer)}}emStrong(e,t,n=""){let s=this.rules.inline.emStrongLDelim.exec(e);if(!s)return;if(s[3]&&n.match(/[\p{L}\p{N}]/u))return;if(!(s[1]||s[2]||"")||!n||this.rules.inline.punctuation.exec(n)){const n=[...s[0]].length-1;let r,i,l=n,o=0;const a="*"===s[0][0]?this.rules.inline.emStrongRDelimAst:this.rules.inline.emStrongRDelimUnd;for(a.lastIndex=0,t=t.slice(-1*e.length+n);null!=(s=a.exec(t));){if(r=s[1]||s[2]||s[3]||s[4]||s[5]||s[6],!r)continue;if(i=[...r].length,s[3]||s[4]){l+=i;continue}if((s[5]||s[6])&&n%3&&!((n+i)%3)){o+=i;continue}if(l-=i,l>0)continue;i=Math.min(i,i+l+o);const t=[...s[0]][0].length,a=e.slice(0,n+s.index+t+i);if(Math.min(n,i)%2){const e=a.slice(1,-1);return{type:"em",raw:a,text:e,tokens:this.lexer.inlineTokens(e)}}const c=a.slice(2,-2);return{type:"strong",raw:a,text:c,tokens:this.lexer.inlineTokens(c)}}}}codespan(e){const t=this.rules.inline.code.exec(e);if(t){let e=t[2].replace(/\n/g," ");const n=/[^ ]/.test(e),s=/^ /.test(e)&&/ $/.test(e);return n&&s&&(e=e.substring(1,e.length-1)),e=c(e,!0),{type:"codespan",raw:t[0],text:e}}}br(e){const t=this.rules.inline.br.exec(e);if(t)return{type:"br",raw:t[0]}}del(e){const t=this.rules.inline.del.exec(e);if(t)return{type:"del",raw:t[0],text:t[2],tokens:this.lexer.inlineTokens(t[2])}}autolink(e){const t=this.rules.inline.autolink.exec(e);if(t){let e,n;return"@"===t[2]?(e=c(t[1]),n="mailto:"+e):(e=c(t[1]),n=e),{type:"link",raw:t[0],text:e,href:n,tokens:[{type:"text",raw:e,text:e}]}}}url(e){let t;if(t=this.rules.inline.url.exec(e)){let e,n;if("@"===t[2])e=c(t[0]),n="mailto:"+e;else{let s;do{s=t[0],t[0]=this.rules.inline._backpedal.exec(t[0])?.[0]??""}while(s!==t[0]);e=c(t[0]),n="www."===t[1]?"http://"+t[0]:t[0]}return{type:"link",raw:t[0],text:e,href:n,tokens:[{type:"text",raw:e,text:e}]}}}inlineText(e){const t=this.rules.inline.text.exec(e);if(t){let e;return e=this.lexer.state.inRawBlock?t[0]:c(t[0]),{type:"text",raw:t[0],text:e}}}}const b=/^ {0,3}((?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/,w=/(?:[*+-]|\d{1,9}[.)])/,m=p(/^(?!bull |blockCode|fences|blockquote|heading|html)((?:.|\n(?!\s*?\n|bull |blockCode|fences|blockquote|heading|html))+?)\n {0,3}(=+|-+) *(?:\n+|$)/).replace(/bull/g,w).replace(/blockCode/g,/(?: {4}| {0,3}\t)/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}/).replace(/html/g,/ {0,3}<[^\n>]+>\n/).getRegex(),y=/^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html|table| +\n)[^\n]+)*)/,$=/(?!\s*\])(?:\\.|[^\[\]\\])+/,z=p(/^ {0,3}\[(label)\]: *(?:\n[ \t]*)?([^<\s][^\s]*|<.*?>)(?:(?: +(?:\n[ \t]*)?| *\n[ \t]*)(title))? *(?:\n+|$)/).replace("label",$).replace("title",/(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/).getRegex(),T=p(/^( {0,3}bull)([ \t][^\n]+?)?(?:\n|$)/).replace(/bull/g,w).getRegex(),R="address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|search|section|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul",_=/|$))/,A=p("^ {0,3}(?:<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?(?:\\?>\\n*|$)|\\n*|$)|\\n*|$)|)[\\s\\S]*?(?:(?:\\n[ \t]*)+\\n|$)|<(?!script|pre|style|textarea)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ \t]*)+\\n|$)|(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ \t]*)+\\n|$))","i").replace("comment",_).replace("tag",R).replace("attribute",/ +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(),S=p(y).replace("hr",b).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("|table","").replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",R).getRegex(),I={blockquote:p(/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/).replace("paragraph",S).getRegex(),code:/^((?: {4}| {0,3}\t)[^\n]+(?:\n(?:[ \t]*(?:\n|$))*)?)+/,def:z,fences:/^ {0,3}(`{3,}(?=[^`\n]*(?:\n|$))|~{3,})([^\n]*)(?:\n|$)(?:|([\s\S]*?)(?:\n|$))(?: {0,3}\1[~`]* *(?=\n|$)|$)/,heading:/^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/,hr:b,html:A,lheading:m,list:T,newline:/^(?:[ \t]*(?:\n|$))+/,paragraph:S,table:k,text:/^[^\n]+/},E=p("^ *([^\\n ].*)\\n {0,3}((?:\\| *)?:?-+:? *(?:\\| *:?-+:? *)*(?:\\| *)?)(?:\\n((?:(?! *\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)").replace("hr",b).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("blockquote"," {0,3}>").replace("code","(?: {4}| {0,3}\t)[^\\n]").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",R).getRegex(),q={...I,table:E,paragraph:p(y).replace("hr",b).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("table",E).replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",R).getRegex()},Z={...I,html:p("^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+? *(?:\\n{2,}|\\s*$)|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))").replace("comment",_).replace(/tag/g,"(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(),def:/^ *\[([^\]]+)\]: *]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,heading:/^(#{1,6})(.*)(?:\n+|$)/,fences:k,lheading:/^(.+?)\n {0,3}(=+|-+) *(?:\n+|$)/,paragraph:p(y).replace("hr",b).replace("heading"," *#{1,6} *[^\n]").replace("lheading",m).replace("|table","").replace("blockquote"," {0,3}>").replace("|fences","").replace("|list","").replace("|html","").replace("|tag","").getRegex()},P=/^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,L=/^( {2,}|\\)\n(?!\s*$)/,v="\\p{P}\\p{S}",Q=p(/^((?![*_])[\spunctuation])/,"u").replace(/punctuation/g,v).getRegex(),B=p(/^(?:\*+(?:((?!\*)[punct])|[^\s*]))|^_+(?:((?!_)[punct])|([^\s_]))/,"u").replace(/punct/g,v).getRegex(),M=p("^[^_*]*?__[^_*]*?\\*[^_*]*?(?=__)|[^*]+(?=[^*])|(?!\\*)[punct](\\*+)(?=[\\s]|$)|[^punct\\s](\\*+)(?!\\*)(?=[punct\\s]|$)|(?!\\*)[punct\\s](\\*+)(?=[^punct\\s])|[\\s](\\*+)(?!\\*)(?=[punct])|(?!\\*)[punct](\\*+)(?!\\*)(?=[punct])|[^punct\\s](\\*+)(?=[^punct\\s])","gu").replace(/punct/g,v).getRegex(),O=p("^[^_*]*?\\*\\*[^_*]*?_[^_*]*?(?=\\*\\*)|[^_]+(?=[^_])|(?!_)[punct](_+)(?=[\\s]|$)|[^punct\\s](_+)(?!_)(?=[punct\\s]|$)|(?!_)[punct\\s](_+)(?=[^punct\\s])|[\\s](_+)(?!_)(?=[punct])|(?!_)[punct](_+)(?!_)(?=[punct])","gu").replace(/punct/g,v).getRegex(),j=p(/\\([punct])/,"gu").replace(/punct/g,v).getRegex(),D=p(/^<(scheme:[^\s\x00-\x1f<>]*|email)>/).replace("scheme",/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/).replace("email",/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/).getRegex(),C=p(_).replace("(?:--\x3e|$)","--\x3e").getRegex(),H=p("^comment|^|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^|^").replace("comment",C).replace("attribute",/\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/).getRegex(),U=/(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/,X=p(/^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/).replace("label",U).replace("href",/<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/).replace("title",/"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/).getRegex(),F=p(/^!?\[(label)\]\[(ref)\]/).replace("label",U).replace("ref",$).getRegex(),N=p(/^!?\[(ref)\](?:\[\])?/).replace("ref",$).getRegex(),G={_backpedal:k,anyPunctuation:j,autolink:D,blockSkip:/\[[^[\]]*?\]\([^\(\)]*?\)|`[^`]*?`|<[^<>]*?>/g,br:L,code:/^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,del:k,emStrongLDelim:B,emStrongRDelimAst:M,emStrongRDelimUnd:O,escape:P,link:X,nolink:N,punctuation:Q,reflink:F,reflinkSearch:p("reflink|nolink(?!\\()","g").replace("reflink",F).replace("nolink",N).getRegex(),tag:H,text:/^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\!!(s=n.call({lexer:this},e,t))&&(e=e.substring(s.raw.length),t.push(s),!0)))))if(s=this.tokenizer.space(e))e=e.substring(s.raw.length),1===s.raw.length&&t.length>0?t[t.length-1].raw+="\n":t.push(s);else if(s=this.tokenizer.code(e))e=e.substring(s.raw.length),r=t[t.length-1],!r||"paragraph"!==r.type&&"text"!==r.type?t.push(s):(r.raw+="\n"+s.raw,r.text+="\n"+s.text,this.inlineQueue[this.inlineQueue.length-1].src=r.text);else if(s=this.tokenizer.fences(e))e=e.substring(s.raw.length),t.push(s);else if(s=this.tokenizer.heading(e))e=e.substring(s.raw.length),t.push(s);else if(s=this.tokenizer.hr(e))e=e.substring(s.raw.length),t.push(s);else if(s=this.tokenizer.blockquote(e))e=e.substring(s.raw.length),t.push(s);else if(s=this.tokenizer.list(e))e=e.substring(s.raw.length),t.push(s);else if(s=this.tokenizer.html(e))e=e.substring(s.raw.length),t.push(s);else if(s=this.tokenizer.def(e))e=e.substring(s.raw.length),r=t[t.length-1],!r||"paragraph"!==r.type&&"text"!==r.type?this.tokens.links[s.tag]||(this.tokens.links[s.tag]={href:s.href,title:s.title}):(r.raw+="\n"+s.raw,r.text+="\n"+s.raw,this.inlineQueue[this.inlineQueue.length-1].src=r.text);else if(s=this.tokenizer.table(e))e=e.substring(s.raw.length),t.push(s);else if(s=this.tokenizer.lheading(e))e=e.substring(s.raw.length),t.push(s);else{if(i=e,this.options.extensions&&this.options.extensions.startBlock){let t=1/0;const n=e.slice(1);let s;this.options.extensions.startBlock.forEach((e=>{s=e.call({lexer:this},n),"number"==typeof s&&s>=0&&(t=Math.min(t,s))})),t<1/0&&t>=0&&(i=e.substring(0,t+1))}if(this.state.top&&(s=this.tokenizer.paragraph(i)))r=t[t.length-1],n&&"paragraph"===r?.type?(r.raw+="\n"+s.raw,r.text+="\n"+s.text,this.inlineQueue.pop(),this.inlineQueue[this.inlineQueue.length-1].src=r.text):t.push(s),n=i.length!==e.length,e=e.substring(s.raw.length);else if(s=this.tokenizer.text(e))e=e.substring(s.raw.length),r=t[t.length-1],r&&"text"===r.type?(r.raw+="\n"+s.raw,r.text+="\n"+s.text,this.inlineQueue.pop(),this.inlineQueue[this.inlineQueue.length-1].src=r.text):t.push(s);else if(e){const t="Infinite loop on byte: "+e.charCodeAt(0);if(this.options.silent){console.error(t);break}throw new Error(t)}}return this.state.top=!0,t}inline(e,t=[]){return this.inlineQueue.push({src:e,tokens:t}),t}inlineTokens(e,t=[]){let n,s,r,i,l,o,a=e;if(this.tokens.links){const e=Object.keys(this.tokens.links);if(e.length>0)for(;null!=(i=this.tokenizer.rules.inline.reflinkSearch.exec(a));)e.includes(i[0].slice(i[0].lastIndexOf("[")+1,-1))&&(a=a.slice(0,i.index)+"["+"a".repeat(i[0].length-2)+"]"+a.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex))}for(;null!=(i=this.tokenizer.rules.inline.blockSkip.exec(a));)a=a.slice(0,i.index)+"["+"a".repeat(i[0].length-2)+"]"+a.slice(this.tokenizer.rules.inline.blockSkip.lastIndex);for(;null!=(i=this.tokenizer.rules.inline.anyPunctuation.exec(a));)a=a.slice(0,i.index)+"++"+a.slice(this.tokenizer.rules.inline.anyPunctuation.lastIndex);for(;e;)if(l||(o=""),l=!1,!(this.options.extensions&&this.options.extensions.inline&&this.options.extensions.inline.some((s=>!!(n=s.call({lexer:this},e,t))&&(e=e.substring(n.raw.length),t.push(n),!0)))))if(n=this.tokenizer.escape(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.tag(e))e=e.substring(n.raw.length),s=t[t.length-1],s&&"text"===n.type&&"text"===s.type?(s.raw+=n.raw,s.text+=n.text):t.push(n);else if(n=this.tokenizer.link(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.reflink(e,this.tokens.links))e=e.substring(n.raw.length),s=t[t.length-1],s&&"text"===n.type&&"text"===s.type?(s.raw+=n.raw,s.text+=n.text):t.push(n);else if(n=this.tokenizer.emStrong(e,a,o))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.codespan(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.br(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.del(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.autolink(e))e=e.substring(n.raw.length),t.push(n);else if(this.state.inLink||!(n=this.tokenizer.url(e))){if(r=e,this.options.extensions&&this.options.extensions.startInline){let t=1/0;const n=e.slice(1);let s;this.options.extensions.startInline.forEach((e=>{s=e.call({lexer:this},n),"number"==typeof s&&s>=0&&(t=Math.min(t,s))})),t<1/0&&t>=0&&(r=e.substring(0,t+1))}if(n=this.tokenizer.inlineText(r))e=e.substring(n.raw.length),"_"!==n.raw.slice(-1)&&(o=n.raw.slice(-1)),l=!0,s=t[t.length-1],s&&"text"===s.type?(s.raw+=n.raw,s.text+=n.text):t.push(n);else if(e){const t="Infinite loop on byte: "+e.charCodeAt(0);if(this.options.silent){console.error(t);break}throw new Error(t)}}else e=e.substring(n.raw.length),t.push(n);return t}}class te{options;parser;constructor(t){this.options=t||e.defaults}space(e){return""}code({text:e,lang:t,escaped:n}){const s=(t||"").match(/^\S*/)?.[0],r=e.replace(/\n$/,"")+"\n";return s?'
'+(n?r:c(r,!0))+"
\n":"
"+(n?r:c(r,!0))+"
\n"}blockquote({tokens:e}){return`
\n${this.parser.parse(e)}
\n`}html({text:e}){return e}heading({tokens:e,depth:t}){return`${this.parser.parseInline(e)}\n`}hr(e){return"
\n"}list(e){const t=e.ordered,n=e.start;let s="";for(let t=0;t\n"+s+"\n"}listitem(e){let t="";if(e.task){const n=this.checkbox({checked:!!e.checked});e.loose?e.tokens.length>0&&"paragraph"===e.tokens[0].type?(e.tokens[0].text=n+" "+e.tokens[0].text,e.tokens[0].tokens&&e.tokens[0].tokens.length>0&&"text"===e.tokens[0].tokens[0].type&&(e.tokens[0].tokens[0].text=n+" "+e.tokens[0].tokens[0].text)):e.tokens.unshift({type:"text",raw:n+" ",text:n+" "}):t+=n+" "}return t+=this.parser.parse(e.tokens,!!e.loose),`
  • ${t}
  • \n`}checkbox({checked:e}){return"'}paragraph({tokens:e}){return`

    ${this.parser.parseInline(e)}

    \n`}table(e){let t="",n="";for(let t=0;t${s}`),"\n\n"+t+"\n"+s+"
    \n"}tablerow({text:e}){return`\n${e}\n`}tablecell(e){const t=this.parser.parseInline(e.tokens),n=e.header?"th":"td";return(e.align?`<${n} align="${e.align}">`:`<${n}>`)+t+`\n`}strong({tokens:e}){return`${this.parser.parseInline(e)}`}em({tokens:e}){return`${this.parser.parseInline(e)}`}codespan({text:e}){return`${e}`}br(e){return"
    "}del({tokens:e}){return`${this.parser.parseInline(e)}`}link({href:e,title:t,tokens:n}){const s=this.parser.parseInline(n),r=u(e);if(null===r)return s;let i='
    ",i}image({href:e,title:t,text:n}){const s=u(e);if(null===s)return n;let r=`${n}{const r=e[s].flat(1/0);n=n.concat(this.walkTokens(r,t))})):e.tokens&&(n=n.concat(this.walkTokens(e.tokens,t)))}}return n}use(...e){const t=this.defaults.extensions||{renderers:{},childTokens:{}};return e.forEach((e=>{const n={...e};if(n.async=this.defaults.async||n.async||!1,e.extensions&&(e.extensions.forEach((e=>{if(!e.name)throw new Error("extension name required");if("renderer"in e){const n=t.renderers[e.name];t.renderers[e.name]=n?function(...t){let s=e.renderer.apply(this,t);return!1===s&&(s=n.apply(this,t)),s}:e.renderer}if("tokenizer"in e){if(!e.level||"block"!==e.level&&"inline"!==e.level)throw new Error("extension level must be 'block' or 'inline'");const n=t[e.level];n?n.unshift(e.tokenizer):t[e.level]=[e.tokenizer],e.start&&("block"===e.level?t.startBlock?t.startBlock.push(e.start):t.startBlock=[e.start]:"inline"===e.level&&(t.startInline?t.startInline.push(e.start):t.startInline=[e.start]))}"childTokens"in e&&e.childTokens&&(t.childTokens[e.name]=e.childTokens)})),n.extensions=t),e.renderer){const t=this.defaults.renderer||new te(this.defaults);for(const n in e.renderer){if(!(n in t))throw new Error(`renderer '${n}' does not exist`);if(["options","parser"].includes(n))continue;const s=n,r=e.renderer[s],i=t[s];t[s]=(...e)=>{let n=r.apply(t,e);return!1===n&&(n=i.apply(t,e)),n||""}}n.renderer=t}if(e.tokenizer){const t=this.defaults.tokenizer||new x(this.defaults);for(const n in e.tokenizer){if(!(n in t))throw new Error(`tokenizer '${n}' does not exist`);if(["options","rules","lexer"].includes(n))continue;const s=n,r=e.tokenizer[s],i=t[s];t[s]=(...e)=>{let n=r.apply(t,e);return!1===n&&(n=i.apply(t,e)),n}}n.tokenizer=t}if(e.hooks){const t=this.defaults.hooks||new re;for(const n in e.hooks){if(!(n in t))throw new Error(`hook '${n}' does not exist`);if(["options","block"].includes(n))continue;const s=n,r=e.hooks[s],i=t[s];re.passThroughHooks.has(n)?t[s]=e=>{if(this.defaults.async)return Promise.resolve(r.call(t,e)).then((e=>i.call(t,e)));const n=r.call(t,e);return i.call(t,n)}:t[s]=(...e)=>{let n=r.apply(t,e);return!1===n&&(n=i.apply(t,e)),n}}n.hooks=t}if(e.walkTokens){const t=this.defaults.walkTokens,s=e.walkTokens;n.walkTokens=function(e){let n=[];return n.push(s.call(this,e)),t&&(n=n.concat(t.call(this,e))),n}}this.defaults={...this.defaults,...n}})),this}setOptions(e){return this.defaults={...this.defaults,...e},this}lexer(e,t){return ee.lex(e,t??this.defaults)}parser(e,t){return se.parse(e,t??this.defaults)}parseMarkdown(e){return(t,n)=>{const s={...n},r={...this.defaults,...s},i=this.onError(!!r.silent,!!r.async);if(!0===this.defaults.async&&!1===s.async)return i(new Error("marked(): The async option was set to true by an extension. Remove async: false from the parse options object to return a Promise."));if(null==t)return i(new Error("marked(): input parameter is undefined or null"));if("string"!=typeof t)return i(new Error("marked(): input parameter is of type "+Object.prototype.toString.call(t)+", string expected"));r.hooks&&(r.hooks.options=r,r.hooks.block=e);const l=r.hooks?r.hooks.provideLexer():e?ee.lex:ee.lexInline,o=r.hooks?r.hooks.provideParser():e?se.parse:se.parseInline;if(r.async)return Promise.resolve(r.hooks?r.hooks.preprocess(t):t).then((e=>l(e,r))).then((e=>r.hooks?r.hooks.processAllTokens(e):e)).then((e=>r.walkTokens?Promise.all(this.walkTokens(e,r.walkTokens)).then((()=>e)):e)).then((e=>o(e,r))).then((e=>r.hooks?r.hooks.postprocess(e):e)).catch(i);try{r.hooks&&(t=r.hooks.preprocess(t));let e=l(t,r);r.hooks&&(e=r.hooks.processAllTokens(e)),r.walkTokens&&this.walkTokens(e,r.walkTokens);let n=o(e,r);return r.hooks&&(n=r.hooks.postprocess(n)),n}catch(e){return i(e)}}}onError(e,t){return n=>{if(n.message+="\nPlease report this to https://github.com/markedjs/marked.",e){const e="

    An error occurred:

    "+c(n.message+"",!0)+"
    ";return t?Promise.resolve(e):e}if(t)return Promise.reject(n);throw n}}}const le=new ie;function oe(e,t){return le.parse(e,t)}oe.options=oe.setOptions=function(e){return le.setOptions(e),oe.defaults=le.defaults,n(oe.defaults),oe},oe.getDefaults=t,oe.defaults=e.defaults,oe.use=function(...e){return le.use(...e),oe.defaults=le.defaults,n(oe.defaults),oe},oe.walkTokens=function(e,t){return le.walkTokens(e,t)},oe.parseInline=le.parseInline,oe.Parser=se,oe.parser=se.parse,oe.Renderer=te,oe.TextRenderer=ne,oe.Lexer=ee,oe.lexer=ee.lex,oe.Tokenizer=x,oe.Hooks=re,oe.parse=oe;const ae=oe.options,ce=oe.setOptions,he=oe.use,pe=oe.walkTokens,ue=oe.parseInline,ke=oe,ge=se.parse,fe=ee.lex;e.Hooks=re,e.Lexer=ee,e.Marked=ie,e.Parser=se,e.Renderer=te,e.TextRenderer=ne,e.Tokenizer=x,e.getDefaults=t,e.lexer=fe,e.marked=oe,e.options=ae,e.parse=ke,e.parseInline=ue,e.parser=ge,e.setOptions=ce,e.use=he,e.walkTokens=pe})); diff --git a/sidebar/sidebar.css b/sidebar/sidebar.css new file mode 100644 index 0000000..a2b067d --- /dev/null +++ b/sidebar/sidebar.css @@ -0,0 +1,90 @@ +body { + 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: 20px; +} + +h1 { + font-size: 24px; + margin-bottom: 20px; + color: #2c3e50; +} + +.btn { + 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; +} + +.btn-primary:hover { + background-color: #2980b9; +} + +.btn-secondary { + background-color: #95a5a6; + color: white; +} + +.btn-secondary:hover { + background-color: #7f8c8d; +} + +.summary-container { + margin-top: 20px; + padding: 15px; + 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; + color: #2c3e50; +} + +#summary p { + margin-bottom: 10px; + line-height: 1.6; +} + +#summary ul, #summary ol { + padding-left: 20px; + margin-bottom: 10px; +} + +#summary code { + background-color: #f0f0f0; + padding: 2px 4px; + border-radius: 3px; + font-family: 'Courier New', Courier, monospace; +} + +#summary pre { + background-color: #f0f0f0; + padding: 10px; + border-radius: 3px; + overflow-x: auto; + font-family: 'Courier New', Courier, monospace; +} + +#open-options { + margin-top: 20px; +} \ No newline at end of file diff --git a/sidebar/sidebar.html b/sidebar/sidebar.html new file mode 100644 index 0000000..2fde5bf --- /dev/null +++ b/sidebar/sidebar.html @@ -0,0 +1,18 @@ + + + + + + + + + +
    +

    OLLAMA Summarizer

    + +
    + +
    + + + \ No newline at end of file diff --git a/sidebar/sidebar.js b/sidebar/sidebar.js new file mode 100644 index 0000000..1738144 --- /dev/null +++ b/sidebar/sidebar.js @@ -0,0 +1,56 @@ +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); + return; + } + + if (response && response.content) { + browser.runtime.sendMessage( + { action: "summarize", content: response.content }, + (response) => { + if (browser.runtime.lastError) { + handleError('Error during summarization: ' + browser.runtime.lastError.message); + return; + } + + if (response && response.summary) { + // Render the Markdown content + summaryDiv.innerHTML = marked.parse(response.summary); + } else if (response && response.error) { + handleError(response.error, response.details); + } else { + handleError("Unexpected response from summarization"); + } + summarizeButton.disabled = false; + } + ); + } else { + 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; + } + }); \ No newline at end of file