c54ae32c7d48

treat markup-only or zero-width contenteditable bodies as blank so a new note with nothing typed is never saved

time
2026-08-13 19:25 UTC · 2 months ago
machine
koh2
agent
deepseek-v4-flash
session
blank note guard
parent
2adb75e4873e
1 file src/popup/views/NotesEditor.tsx+12 −2
src/popup/views/NotesEditor.tsx+12 −2
@@ -33,6 +33,16 @@3333   let saveTimer: number | null = null;3434   let savedFadeTimer: number | null = null;3535 36+  // An "empty" contenteditable body frequently serialises to "<br>", a37+  // zero-width space, &nbsp;, or whitespace-only markup rather than "". Trim38+  // alone can't catch those, so strip tags/entities/formatting and check for39+  // any actual visible character before deciding a note has content.40+  function bodyIsBlank(html: string): boolean {41+    return !html42+      .replace(/<[^>]*>|&nbsp;|&#160;|\u200b|\ufeff/g, "")43+      .replace(/\s+/g, "");44+  }45+3646   function scheduleAutosave() {3747     setDirty(true);3848     if (saveTimer) clearTimeout(saveTimer);@@ -43,7 +53,7 @@4353     const t = title();4454     const b = body();4555     // Don't save if genuinely empty (new note, nothing typed)46-    if (!t.trim() && !b.trim()) return;56+    if (!t.trim() && bodyIsBlank(b)) return;4757     const res = await sendMessage({4858       type: "SAVE_NOTE",4959       payload: { id: noteId(), title: t || "Untitled", body: b },@@ -71,7 +81,7 @@7181   async function handleBack() {7282     // Await the final save before navigating away — fire-and-forget would let7383     // the component unmount before the message round-trip completes.74-    if (dirty() && (title().trim() || body().trim())) {84+    if (dirty() && (title().trim() || !bodyIsBlank(body()))) {7585       if (saveTimer) { clearTimeout(saveTimer); saveTimer = null; }7686       await save();7787     }

view file