Replace marked.min.js with the marked npm package and update all references accordingly.
This commit is contained in:
@@ -18,7 +18,7 @@ Three-part Chrome extension:
|
||||
- **Runtime**: Chrome MV3 (Manifest V3)
|
||||
- **Language**: Vanilla JavaScript (ES6+), no frontend framework
|
||||
- **Styling**: Tailwind CSS v4 + PostCSS + Autoprefixer; CSS variables for theming; dark mode via `prefers-color-scheme`
|
||||
- **Markdown**: Custom HTML→Markdown converter in `content.js`; `marked.min.js` for preview rendering
|
||||
- **Markdown**: Custom HTML→Markdown converter in `content.js`; `marked` npm package for preview rendering
|
||||
- **Build**: Vite v8 with `vite-plugin-static-copy`
|
||||
|
||||
## Build & Development
|
||||
@@ -66,7 +66,6 @@ src/
|
||||
├── settings.html/.js/.css # Options page
|
||||
├── content.js # Page content extractor
|
||||
├── background.js # Service worker
|
||||
├── marked.min.js # Bundled Markdown renderer
|
||||
└── icons/ # 16, 32, 48, 128px PNG icons
|
||||
dist/ # Build output (gitignored)
|
||||
vite.config.js
|
||||
|
||||
15
package-lock.json
generated
15
package-lock.json
generated
@@ -8,6 +8,9 @@
|
||||
"name": "memos-extension",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"marked": "^17.0.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4.2.1",
|
||||
"@tailwindcss/typography": "^0.5.19",
|
||||
@@ -1614,6 +1617,18 @@
|
||||
"@jridgewell/sourcemap-codec": "^1.5.5"
|
||||
}
|
||||
},
|
||||
"node_modules/marked": {
|
||||
"version": "17.0.4",
|
||||
"resolved": "https://registry.npmjs.org/marked/-/marked-17.0.4.tgz",
|
||||
"integrity": "sha512-NOmVMM+KAokHMvjWmC5N/ZOvgmSWuqJB8FoYI019j4ogb/PeRMKoKIjReZ2w3376kkA8dSJIP8uD993Kxc0iRQ==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"marked": "bin/marked.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.11",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
|
||||
|
||||
@@ -19,5 +19,8 @@
|
||||
"tailwindcss": "^4.2.1",
|
||||
"vite": "^8.0.0",
|
||||
"vite-plugin-static-copy": "^3.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"marked": "^17.0.4"
|
||||
}
|
||||
}
|
||||
|
||||
146
src/marked.min.js
vendored
146
src/marked.min.js
vendored
@@ -1,146 +0,0 @@
|
||||
/*!
|
||||
* marked-mini - minimal Markdown renderer for Memos Clipper
|
||||
* Supports: headings, bold, italic, strike, code, pre, blockquote,
|
||||
* links, images, ul, ol, hr, tables, line breaks
|
||||
*/
|
||||
(function(global){
|
||||
"use strict";
|
||||
function escape(s){return s.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""");}
|
||||
|
||||
function parseInline(s){
|
||||
// code spans
|
||||
s = s.replace(/`([^`]+)`/g,(_,c)=>`<code>${escape(c)}</code>`);
|
||||
// bold+italic
|
||||
s = s.replace(/\*\*\*(.+?)\*\*\*/g,"<strong><em>$1</em></strong>");
|
||||
// bold
|
||||
s = s.replace(/\*\*(.+?)\*\*/g,"<strong>$1</strong>");
|
||||
s = s.replace(/__(.+?)__/g,"<strong>$1</strong>");
|
||||
// italic
|
||||
s = s.replace(/\*(.+?)\*/g,"<em>$1</em>");
|
||||
s = s.replace(/_([^_]+)_/g,"<em>$1</em>");
|
||||
// strikethrough
|
||||
s = s.replace(/~~(.+?)~~/g,"<del>$1</del>");
|
||||
// images
|
||||
s = s.replace(/!\[([^\]]*)\]\(([^)]+)\)/g,(_,alt,src)=>`<img src="${escape(src)}" alt="${escape(alt)}">`);
|
||||
// links
|
||||
s = s.replace(/\[([^\]]+)\]\(([^)]+)\)/g,(_,txt,href)=>`<a href="${escape(href)}" target="_blank">${txt}</a>`);
|
||||
// auto-link
|
||||
s = s.replace(/(?<!["\(])(https?:\/\/[^\s<>)]+)/g,url=>`<a href="${escape(url)}" target="_blank">${escape(url)}</a>`);
|
||||
// hard break
|
||||
s = s.replace(/ \n/g,"<br>");
|
||||
return s;
|
||||
}
|
||||
|
||||
function parse(src){
|
||||
const lines = src.split("\n");
|
||||
const out = [];
|
||||
let i = 0;
|
||||
|
||||
while(i < lines.length){
|
||||
const line = lines[i];
|
||||
|
||||
// Fenced code block
|
||||
const fenceM = line.match(/^```(\w*)/);
|
||||
if(fenceM){
|
||||
const lang = fenceM[1]||"";
|
||||
const code = [];
|
||||
i++;
|
||||
while(i<lines.length && !lines[i].startsWith("```")){
|
||||
code.push(lines[i]);
|
||||
i++;
|
||||
}
|
||||
i++; // skip closing ```
|
||||
out.push(`<pre><code class="language-${escape(lang)}">${escape(code.join("\n"))}</code></pre>`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Headings
|
||||
const hM = line.match(/^(#{1,6})\s+(.*)/);
|
||||
if(hM){
|
||||
const lvl = hM[1].length;
|
||||
out.push(`<h${lvl}>${parseInline(hM[2])}</h${lvl}>`);
|
||||
i++; continue;
|
||||
}
|
||||
|
||||
// HR
|
||||
if(/^[-*_]{3,}\s*$/.test(line)){
|
||||
out.push("<hr>");
|
||||
i++; continue;
|
||||
}
|
||||
|
||||
// Blockquote
|
||||
if(line.startsWith("> ")){
|
||||
const bq = [];
|
||||
while(i<lines.length && lines[i].startsWith("> ")){
|
||||
bq.push(lines[i].slice(2));
|
||||
i++;
|
||||
}
|
||||
out.push(`<blockquote>${parse(bq.join("\n"))}</blockquote>`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Unordered list
|
||||
if(/^[-*+]\s/.test(line)){
|
||||
const items = [];
|
||||
while(i<lines.length && /^[-*+]\s/.test(lines[i])){
|
||||
items.push(`<li>${parseInline(lines[i].replace(/^[-*+]\s/,""))}</li>`);
|
||||
i++;
|
||||
}
|
||||
out.push(`<ul>${items.join("")}</ul>`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ordered list
|
||||
if(/^\d+\.\s/.test(line)){
|
||||
const items = [];
|
||||
while(i<lines.length && /^\d+\.\s/.test(lines[i])){
|
||||
items.push(`<li>${parseInline(lines[i].replace(/^\d+\.\s/,""))}</li>`);
|
||||
i++;
|
||||
}
|
||||
out.push(`<ol>${items.join("")}</ol>`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Table (| col | col |)
|
||||
if(line.startsWith("|") && i+1<lines.length && /^\|[-| :]+\|/.test(lines[i+1])){
|
||||
const headers = line.split("|").filter((_,idx,a)=>idx>0&&idx<a.length-1).map(h=>`<th>${parseInline(h.trim())}</th>`);
|
||||
i+=2; // skip header + separator
|
||||
const rows = [];
|
||||
while(i<lines.length && lines[i].startsWith("|")){
|
||||
const cells = lines[i].split("|").filter((_,idx,a)=>idx>0&&idx<a.length-1).map(c=>`<td>${parseInline(c.trim())}</td>`);
|
||||
rows.push(`<tr>${cells.join("")}</tr>`);
|
||||
i++;
|
||||
}
|
||||
out.push(`<table><thead><tr>${headers.join("")}</tr></thead><tbody>${rows.join("")}</tbody></table>`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Empty line
|
||||
if(line.trim()===""){
|
||||
i++; continue;
|
||||
}
|
||||
|
||||
// Paragraph — collect until blank line
|
||||
const para = [];
|
||||
while(i<lines.length && lines[i].trim()!==""){
|
||||
// stop on block elements
|
||||
if(/^(#{1,6}\s|```|>|[-*+]\s|\d+\.\s|[-*_]{3,}\s*$)/.test(lines[i])) break;
|
||||
para.push(lines[i]);
|
||||
i++;
|
||||
}
|
||||
if(para.length) out.push(`<p>${parseInline(para.join(" "))}</p>`);
|
||||
}
|
||||
return out.join("\n");
|
||||
}
|
||||
|
||||
global.marked = {
|
||||
parse: function(src, opts) {
|
||||
const breaks = opts && opts.breaks;
|
||||
// With breaks:true, single newlines in paragraphs become <br>
|
||||
if (breaks) {
|
||||
// Pre-process: single \n inside paragraph text → two spaces + \n (markdown hard break)
|
||||
src = src.replace(/([^\n])\n([^\n])/g, "$1 \n$2");
|
||||
}
|
||||
return parse(src);
|
||||
}
|
||||
};})(window);
|
||||
@@ -133,7 +133,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/marked.min.js"></script>
|
||||
<script src="/popup.js" type="module"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// popup.js
|
||||
"use strict";
|
||||
|
||||
import { marked } from "marked";
|
||||
|
||||
// ── State ───────────────────────────────────────────────────────────────────
|
||||
const state = {
|
||||
markdown: "",
|
||||
@@ -172,12 +174,7 @@ function sanitizeHtml(html) {
|
||||
|
||||
function renderPreview() {
|
||||
state.markdown = mdEditor.value;
|
||||
if (typeof marked !== "undefined") {
|
||||
mdPreview.innerHTML = sanitizeHtml(marked.parse(state.markdown, { breaks: true }));
|
||||
} else {
|
||||
// Fallback: basic escaping
|
||||
mdPreview.innerHTML = `<pre style="white-space:pre-wrap">${escHtml(state.markdown)}</pre>`;
|
||||
}
|
||||
mdPreview.innerHTML = sanitizeHtml(marked.parse(state.markdown, { breaks: true }));
|
||||
}
|
||||
|
||||
function escHtml(s) {
|
||||
|
||||
@@ -24,8 +24,7 @@ export default defineConfig({
|
||||
viteStaticCopy({
|
||||
targets: [
|
||||
{ src: 'manifest.json', dest: '.' },
|
||||
{ src: 'icons/*', dest: 'icons' },
|
||||
{ src: 'marked.min.js', dest: '.' }
|
||||
{ src: 'icons/*', dest: 'icons' }
|
||||
]
|
||||
})
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user