Auto-commited changes
This commit is contained in:
@@ -5,7 +5,7 @@ window.__memosClipperLoaded = true;
|
|||||||
|
|
||||||
(function () {
|
(function () {
|
||||||
// ── Turndown-lite: a minimal but solid HTML→Markdown converter ──────────────
|
// ── Turndown-lite: a minimal but solid HTML→Markdown converter ──────────────
|
||||||
function htmlToMarkdown(element, isSelection = false) {
|
function htmlToMarkdown(element, isSelection = false, stripLinks = false) {
|
||||||
const clone = element.cloneNode(true);
|
const clone = element.cloneNode(true);
|
||||||
|
|
||||||
// Remove unwanted elements — comprehensive list covering real-world sites
|
// Remove unwanted elements — comprehensive list covering real-world sites
|
||||||
@@ -54,15 +54,40 @@ window.__memosClipperLoaded = true;
|
|||||||
// Also remove elements that are visually hidden via inline style
|
// Also remove elements that are visually hidden via inline style
|
||||||
clone.querySelectorAll('[style*="display:none"],[style*="display: none"],[style*="visibility:hidden"]')
|
clone.querySelectorAll('[style*="display:none"],[style*="display: none"],[style*="visibility:hidden"]')
|
||||||
.forEach((el) => el.remove());
|
.forEach((el) => el.remove());
|
||||||
|
|
||||||
|
// Remove link-dense blocks (navigation menus, ad link lists, etc.)
|
||||||
|
// Collect candidates first to avoid mid-iteration detached-node issues.
|
||||||
|
// Only target outer chrome elements (nav, aside, header, footer, div, section)
|
||||||
|
// not content containers like article/main, to avoid stripping TOCs in prose.
|
||||||
|
const linkDenseCandidates = Array.from(
|
||||||
|
clone.querySelectorAll('nav, aside, header, footer, div, section')
|
||||||
|
).filter((el) => {
|
||||||
|
// Skip if inside the primary content container
|
||||||
|
if (el.closest('article, main, [role="main"]')) return false;
|
||||||
|
const totalText = (el.textContent || '').trim().length;
|
||||||
|
if (totalText < 30) return false; // too short to judge
|
||||||
|
const linkText = Array.from(el.querySelectorAll('a'))
|
||||||
|
.reduce((sum, a) => sum + (a.textContent || '').trim().length, 0);
|
||||||
|
if (linkText / totalText <= 0.65) return false;
|
||||||
|
// Require that the element has little direct (non-link) text of its own
|
||||||
|
const directText = Array.from(el.childNodes)
|
||||||
|
.filter((n) => n.nodeType === Node.TEXT_NODE)
|
||||||
|
.reduce((sum, n) => sum + n.textContent.trim().length, 0);
|
||||||
|
return directText < totalText * 0.25;
|
||||||
|
});
|
||||||
|
// Remove outermost candidates only (skip those already inside a removed ancestor)
|
||||||
|
linkDenseCandidates.forEach((el) => {
|
||||||
|
if (el.isConnected) el.remove();
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
// In selection mode, we still want to remove script/style tags if any
|
// In selection mode, we still want to remove script/style tags if any
|
||||||
clone.querySelectorAll('script, style, noscript, template').forEach((el) => el.remove());
|
clone.querySelectorAll('script, style, noscript, template').forEach((el) => el.remove());
|
||||||
}
|
}
|
||||||
|
|
||||||
return nodeToMd(clone).replace(/\n{3,}/g, "\n\n").trim();
|
return nodeToMd(clone, { listDepth: 0, ordered: false, index: 0 }, stripLinks).replace(/\n{3,}/g, "\n\n").trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
function nodeToMd(node, ctx = { listDepth: 0, ordered: false, index: 0 }) {
|
function nodeToMd(node, ctx = { listDepth: 0, ordered: false, index: 0 }, stripLinks = false) {
|
||||||
if (node.nodeType === Node.TEXT_NODE) {
|
if (node.nodeType === Node.TEXT_NODE) {
|
||||||
return node.textContent.replace(/\s+/g, " ");
|
return node.textContent.replace(/\s+/g, " ");
|
||||||
}
|
}
|
||||||
@@ -71,7 +96,7 @@ window.__memosClipperLoaded = true;
|
|||||||
const tag = node.tagName.toLowerCase();
|
const tag = node.tagName.toLowerCase();
|
||||||
const children = () =>
|
const children = () =>
|
||||||
Array.from(node.childNodes)
|
Array.from(node.childNodes)
|
||||||
.map((c) => nodeToMd(c, ctx))
|
.map((c) => nodeToMd(c, ctx, stripLinks))
|
||||||
.join("");
|
.join("");
|
||||||
|
|
||||||
switch (tag) {
|
switch (tag) {
|
||||||
@@ -93,7 +118,8 @@ window.__memosClipperLoaded = true;
|
|||||||
case "del": return `~~${children()}~~`;
|
case "del": return `~~${children()}~~`;
|
||||||
case "code": {
|
case "code": {
|
||||||
const text = node.textContent;
|
const text = node.textContent;
|
||||||
return text.includes("`") ? `\`\`${text}\`\`` : `\`${text}\``;
|
if (text.includes("`")) return `\`\` ${text} \`\``;
|
||||||
|
return `\`${text}\``;
|
||||||
}
|
}
|
||||||
case "pre": {
|
case "pre": {
|
||||||
const codeEl = node.querySelector("code");
|
const codeEl = node.querySelector("code");
|
||||||
@@ -110,8 +136,9 @@ window.__memosClipperLoaded = true;
|
|||||||
.join("\n")}\n\n`;
|
.join("\n")}\n\n`;
|
||||||
|
|
||||||
case "a": {
|
case "a": {
|
||||||
const href = node.getAttribute("href") || "";
|
|
||||||
const text = children().trim();
|
const text = children().trim();
|
||||||
|
if (stripLinks) return text; // just the anchor text, no URL
|
||||||
|
const href = node.getAttribute("href") || "";
|
||||||
if (!text) return href;
|
if (!text) return href;
|
||||||
try {
|
try {
|
||||||
const abs = new URL(href, location.href).href;
|
const abs = new URL(href, location.href).href;
|
||||||
@@ -134,24 +161,24 @@ window.__memosClipperLoaded = true;
|
|||||||
|
|
||||||
case "ul": {
|
case "ul": {
|
||||||
const lines = Array.from(node.children)
|
const lines = Array.from(node.children)
|
||||||
.map((li) => `${" ".repeat(ctx.listDepth)}- ${nodeToMd(li, { ...ctx, listDepth: ctx.listDepth + 1 }).trim()}`)
|
.map((li) => `${" ".repeat(ctx.listDepth)}- ${nodeToMd(li, { ...ctx, listDepth: ctx.listDepth + 1 }, stripLinks).trim()}`)
|
||||||
.join("\n");
|
.join("\n");
|
||||||
return `\n\n${lines}\n\n`;
|
return `\n\n${lines}\n\n`;
|
||||||
}
|
}
|
||||||
case "ol": {
|
case "ol": {
|
||||||
const lines = Array.from(node.children)
|
const lines = Array.from(node.children)
|
||||||
.map((li, i) => `${" ".repeat(ctx.listDepth)}${i + 1}. ${nodeToMd(li, { ...ctx, listDepth: ctx.listDepth + 1 }).trim()}`)
|
.map((li, i) => `${" ".repeat(ctx.listDepth)}${i + 1}. ${nodeToMd(li, { ...ctx, listDepth: ctx.listDepth + 1 }, stripLinks).trim()}`)
|
||||||
.join("\n");
|
.join("\n");
|
||||||
return `\n\n${lines}\n\n`;
|
return `\n\n${lines}\n\n`;
|
||||||
}
|
}
|
||||||
case "li": return children();
|
case "li": return children();
|
||||||
|
|
||||||
case "table": return convertTable(node);
|
case "table": return convertTable(node, stripLinks);
|
||||||
|
|
||||||
case "figure": {
|
case "figure": {
|
||||||
const img = node.querySelector("img");
|
const img = node.querySelector("img");
|
||||||
const caption = node.querySelector("figcaption");
|
const caption = node.querySelector("figcaption");
|
||||||
let md = img ? nodeToMd(img, ctx) : children();
|
let md = img ? nodeToMd(img, ctx, stripLinks) : children();
|
||||||
if (caption) md += `\n*${caption.textContent.trim()}*`;
|
if (caption) md += `\n*${caption.textContent.trim()}*`;
|
||||||
return `\n\n${md}\n\n`;
|
return `\n\n${md}\n\n`;
|
||||||
}
|
}
|
||||||
@@ -178,13 +205,16 @@ window.__memosClipperLoaded = true;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function convertTable(table) {
|
function convertTable(table, stripLinks = false) {
|
||||||
const rows = Array.from(table.querySelectorAll("tr"));
|
const rows = Array.from(table.querySelectorAll("tr"));
|
||||||
if (!rows.length) return "";
|
if (!rows.length) return "";
|
||||||
const toRow = (tr) =>
|
const toRow = (tr) =>
|
||||||
"| " +
|
"| " +
|
||||||
Array.from(tr.querySelectorAll("th,td"))
|
Array.from(tr.querySelectorAll("th,td"))
|
||||||
.map((c) => c.textContent.trim().replace(/\|/g, "\\|"))
|
.map((c) => {
|
||||||
|
const text = stripLinks ? c.textContent.trim() : nodeToMd(c).trim();
|
||||||
|
return text.replace(/\|/g, "\\|");
|
||||||
|
})
|
||||||
.join(" | ") +
|
.join(" | ") +
|
||||||
" |";
|
" |";
|
||||||
const header = toRow(rows[0]);
|
const header = toRow(rows[0]);
|
||||||
@@ -234,6 +264,7 @@ window.__memosClipperLoaded = true;
|
|||||||
let markdown = "";
|
let markdown = "";
|
||||||
let images = [];
|
let images = [];
|
||||||
let title = document.title || location.href;
|
let title = document.title || location.href;
|
||||||
|
const stripLinks = !!msg.stripLinks;
|
||||||
|
|
||||||
if (msg.mode === "selection") {
|
if (msg.mode === "selection") {
|
||||||
const sel = window.getSelection();
|
const sel = window.getSelection();
|
||||||
@@ -241,7 +272,7 @@ window.__memosClipperLoaded = true;
|
|||||||
const frag = sel.getRangeAt(0).cloneContents();
|
const frag = sel.getRangeAt(0).cloneContents();
|
||||||
const div = document.createElement("div");
|
const div = document.createElement("div");
|
||||||
div.appendChild(frag);
|
div.appendChild(frag);
|
||||||
markdown = htmlToMarkdown(div, true);
|
markdown = htmlToMarkdown(div, true, stripLinks);
|
||||||
images = extractImages(div);
|
images = extractImages(div);
|
||||||
} else {
|
} else {
|
||||||
markdown = "";
|
markdown = "";
|
||||||
@@ -253,7 +284,7 @@ window.__memosClipperLoaded = true;
|
|||||||
document.querySelector("main") ||
|
document.querySelector("main") ||
|
||||||
document.querySelector('[role="main"]') ||
|
document.querySelector('[role="main"]') ||
|
||||||
document.body;
|
document.body;
|
||||||
markdown = htmlToMarkdown(root);
|
markdown = htmlToMarkdown(root, false, stripLinks);
|
||||||
images = extractImages(root);
|
images = extractImages(root);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -315,6 +315,16 @@ header {
|
|||||||
.img-chip .remove-img:hover { color: var(--error); }
|
.img-chip .remove-img:hover { color: var(--error); }
|
||||||
.img-chip.skipped { opacity: .4; }
|
.img-chip.skipped { opacity: .4; }
|
||||||
|
|
||||||
|
/* ── Options row ── */
|
||||||
|
#options-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 6px 14px;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
background: var(--surface);
|
||||||
|
}
|
||||||
|
|
||||||
/* ── Tags row ── */
|
/* ── Tags row ── */
|
||||||
#tags-row {
|
#tags-row {
|
||||||
padding: 6px 14px;
|
padding: 6px 14px;
|
||||||
@@ -414,5 +424,21 @@ select option { background: var(--surface); }
|
|||||||
::-webkit-scrollbar-thumb { background: var(--border); border-radius: 4px; }
|
::-webkit-scrollbar-thumb { background: var(--border); border-radius: 4px; }
|
||||||
::-webkit-scrollbar-thumb:hover { background: var(--text-muted); }
|
::-webkit-scrollbar-thumb:hover { background: var(--text-muted); }
|
||||||
|
|
||||||
/* mode badge */
|
/* mode button */
|
||||||
#mode-toggle.mode-selection { color: var(--accent); background: var(--accent-dim); border-color: var(--accent); }
|
.mode-btn {
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
padding: 4px 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--text-dim);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
font-family: var(--font);
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: all .12s;
|
||||||
|
}
|
||||||
|
.mode-btn:hover { border-color: var(--accent); color: var(--text); }
|
||||||
|
.mode-btn.mode-selection { color: var(--accent); background: var(--accent-dim); border-color: var(--accent); }
|
||||||
|
|||||||
@@ -34,10 +34,11 @@
|
|||||||
<span id="page-title" class="page-title font-semibold text-gray-800 dark:text-gray-200 truncate max-w-[180px]">Clip to Memos</span>
|
<span id="page-title" class="page-title font-semibold text-gray-800 dark:text-gray-200 truncate max-w-[180px]">Clip to Memos</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-actions flex space-x-1">
|
<div class="header-actions flex space-x-1">
|
||||||
<button id="mode-toggle" class="icon-btn p-1.5 rounded hover:bg-gray-100 text-gray-500 transition" title="Switch clip mode">
|
<button id="mode-toggle" class="mode-btn flex items-center space-x-1 px-2 py-1 rounded hover:bg-gray-100 dark:hover:bg-gray-800 text-gray-500 transition text-xs" title="Switch clip mode">
|
||||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="15" height="15">
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="13" height="13">
|
||||||
<path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"/>
|
<path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
<span id="mode-label">Full page</span>
|
||||||
</button>
|
</button>
|
||||||
<button id="settings-btn" class="icon-btn p-1.5 rounded hover:bg-gray-100 text-gray-500 transition" title="Settings">
|
<button id="settings-btn" class="icon-btn p-1.5 rounded hover:bg-gray-100 text-gray-500 transition" title="Settings">
|
||||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="15" height="15">
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="15" height="15">
|
||||||
@@ -78,6 +79,14 @@
|
|||||||
<div id="images-list"></div>
|
<div id="images-list"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- options row -->
|
||||||
|
<div id="options-row">
|
||||||
|
<label class="toggle-label" title="Remove hyperlinks from the clipped text, keeping only the link labels">
|
||||||
|
<input type="checkbox" id="strip-links" />
|
||||||
|
strip links
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- tag input -->
|
<!-- tag input -->
|
||||||
<div id="tags-row">
|
<div id="tags-row">
|
||||||
<input id="tags-input" type="text" placeholder="#tag1 #tag2 …" spellcheck="false" />
|
<input id="tags-input" type="text" placeholder="#tag1 #tag2 …" spellcheck="false" />
|
||||||
@@ -124,7 +133,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/marked.min.js" type="module"></script>
|
<script src="/marked.min.js"></script>
|
||||||
<script src="/popup.js" type="module"></script>
|
<script src="/popup.js" type="module"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
41
src/popup.js
41
src/popup.js
@@ -25,6 +25,7 @@ const imgCount = document.getElementById("img-count");
|
|||||||
const pageTitle = document.getElementById("page-title");
|
const pageTitle = document.getElementById("page-title");
|
||||||
const visibilityEl = document.getElementById("visibility-select");
|
const visibilityEl = document.getElementById("visibility-select");
|
||||||
const attachCheck = document.getElementById("attach-images");
|
const attachCheck = document.getElementById("attach-images");
|
||||||
|
const stripLinksCheck = document.getElementById("strip-links");
|
||||||
const modeToggle = document.getElementById("mode-toggle");
|
const modeToggle = document.getElementById("mode-toggle");
|
||||||
const sendBtn = document.getElementById("send-btn");
|
const sendBtn = document.getElementById("send-btn");
|
||||||
const reloadBtn = document.getElementById("reload-btn");
|
const reloadBtn = document.getElementById("reload-btn");
|
||||||
@@ -61,6 +62,7 @@ async function getActiveTab() {
|
|||||||
state.mode = state.settings.clipMode || "page";
|
state.mode = state.settings.clipMode || "page";
|
||||||
if (state.settings.visibility) visibilityEl.value = state.settings.visibility;
|
if (state.settings.visibility) visibilityEl.value = state.settings.visibility;
|
||||||
if (state.settings.includeImages === false) attachCheck.checked = false;
|
if (state.settings.includeImages === false) attachCheck.checked = false;
|
||||||
|
if (state.settings.includeTags) tagsInput.value = "#clipped";
|
||||||
|
|
||||||
updateModeButton();
|
updateModeButton();
|
||||||
await clip();
|
await clip();
|
||||||
@@ -82,7 +84,7 @@ async function clip() {
|
|||||||
} catch (_) { /* already injected or restricted page */ }
|
} catch (_) { /* already injected or restricted page */ }
|
||||||
|
|
||||||
const response = await new Promise((resolve, reject) => {
|
const response = await new Promise((resolve, reject) => {
|
||||||
chrome.tabs.sendMessage(tab.id, { action: "getContent", mode: state.mode }, (res) => {
|
chrome.tabs.sendMessage(tab.id, { action: "getContent", mode: state.mode, stripLinks: stripLinksCheck.checked }, (res) => {
|
||||||
if (chrome.runtime.lastError) reject(new Error(chrome.runtime.lastError.message));
|
if (chrome.runtime.lastError) reject(new Error(chrome.runtime.lastError.message));
|
||||||
else resolve(res);
|
else resolve(res);
|
||||||
});
|
});
|
||||||
@@ -249,17 +251,17 @@ mdEditor.addEventListener("input", updateCharCounter);
|
|||||||
// ── Tags input ────────────────────────────────────────────────────────────────
|
// ── Tags input ────────────────────────────────────────────────────────────────
|
||||||
const tagsInput = document.getElementById("tags-input");
|
const tagsInput = document.getElementById("tags-input");
|
||||||
|
|
||||||
// Load saved tags default
|
|
||||||
getSettings().then((s) => {
|
|
||||||
if (s.includeTags) tagsInput.value = "#clipped";
|
|
||||||
});
|
|
||||||
|
|
||||||
// ── Mode toggle ───────────────────────────────────────────────────────────────
|
// ── Mode toggle ───────────────────────────────────────────────────────────────
|
||||||
|
const modeLabel = document.getElementById("mode-label");
|
||||||
|
|
||||||
function updateModeButton() {
|
function updateModeButton() {
|
||||||
modeToggle.title = state.mode === "page"
|
const isSelection = state.mode === "selection";
|
||||||
? "Currently: full page — click for selection mode"
|
modeToggle.title = isSelection
|
||||||
: "Currently: selection — click for full page mode";
|
? "Currently: selection — click for full page mode"
|
||||||
modeToggle.classList.toggle("mode-selection", state.mode === "selection");
|
: "Currently: full page — click for selection mode";
|
||||||
|
modeToggle.classList.toggle("mode-selection", isSelection);
|
||||||
|
modeLabel.textContent = isSelection ? "Selection" : "Full page";
|
||||||
}
|
}
|
||||||
|
|
||||||
modeToggle.addEventListener("click", async () => {
|
modeToggle.addEventListener("click", async () => {
|
||||||
@@ -268,6 +270,9 @@ modeToggle.addEventListener("click", async () => {
|
|||||||
await clip();
|
await clip();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ── Strip links toggle ────────────────────────────────────────────────────────
|
||||||
|
stripLinksCheck.addEventListener("change", () => clip());
|
||||||
|
|
||||||
// ── Reload ────────────────────────────────────────────────────────────────────
|
// ── Reload ────────────────────────────────────────────────────────────────────
|
||||||
reloadBtn.addEventListener("click", () => clip());
|
reloadBtn.addEventListener("click", () => clip());
|
||||||
|
|
||||||
@@ -295,6 +300,14 @@ sendBtn.addEventListener("click", async () => {
|
|||||||
sendBtn.disabled = true;
|
sendBtn.disabled = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// Validate Memos URL scheme to prevent accidental non-HTTP destinations
|
||||||
|
try {
|
||||||
|
const u = new URL(baseUrl);
|
||||||
|
if (u.protocol !== "http:" && u.protocol !== "https:") throw new Error();
|
||||||
|
} catch {
|
||||||
|
throw new Error("Invalid Memos URL in settings. Must start with http:// or https://");
|
||||||
|
}
|
||||||
|
|
||||||
// 1. Create the memo first (with original image URLs)
|
// 1. Create the memo first (with original image URLs)
|
||||||
sendBtn.textContent = "Creating memo…";
|
sendBtn.textContent = "Creating memo…";
|
||||||
|
|
||||||
@@ -342,7 +355,9 @@ sendBtn.addEventListener("click", async () => {
|
|||||||
const attachmentUrl = `${baseUrl}/file/${attachName}`;
|
const attachmentUrl = `${baseUrl}/file/${attachName}`;
|
||||||
const escapedUrl = origUrl.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
const escapedUrl = origUrl.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||||
const re = new RegExp(`!\\[(.*?)\\]\\(${escapedUrl}\\)`, 'g');
|
const re = new RegExp(`!\\[(.*?)\\]\\(${escapedUrl}\\)`, 'g');
|
||||||
contentWithImages = contentWithImages.replace(re, ``);
|
// Escape $ in replacement to prevent backreference interpretation
|
||||||
|
const safeReplacement = `})`;
|
||||||
|
contentWithImages = contentWithImages.replace(re, safeReplacement);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append any uploaded attachments not already referenced in the markdown
|
// Append any uploaded attachments not already referenced in the markdown
|
||||||
@@ -353,7 +368,7 @@ sendBtn.addEventListener("click", async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await fetch(`${baseUrl}/api/v1/${memoName}`, {
|
const patchRes = await fetch(`${baseUrl}/api/v1/${memoName}`, {
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
@@ -361,6 +376,10 @@ sendBtn.addEventListener("click", async () => {
|
|||||||
},
|
},
|
||||||
body: JSON.stringify({ content: contentWithImages }),
|
body: JSON.stringify({ content: contentWithImages }),
|
||||||
});
|
});
|
||||||
|
if (!patchRes.ok) {
|
||||||
|
const txt = await patchRes.text();
|
||||||
|
console.warn(`Memo patch failed ${patchRes.status}: ${txt.slice(0, 200)}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let memoId = "";
|
let memoId = "";
|
||||||
|
|||||||
Reference in New Issue
Block a user