PatchHub

← patchhub/notes

Download all notes as a text file

Published ✓ Safe by @theo 0 users1 vote 2 files changed (+50 −0)

Adds a "⬇ Download all notes" button at the bottom of the notes list. Clicking it saves every note into one text file named with today's date, which works as a simple backup. The button is greyed out when there are no notes.

What it does

- Add a full-width button labeled "⬇ Download all notes" in a new section at the bottom of the sidebar, below the notes list, separated by a top border. Tooltip: "Save a copy of all your notes as a text file". - The button is disabled (opacity about 0.5, default cursor, no hover border change) whenever there are zero notes, and enabled otherwise. This is updated on every render. - On click: if an autosave is pending, save immediately first. If there are no notes, do nothing. - Build one text file with all notes in the same order as the app list (newest first). Each note block contains: the note's display title; an underline of "-" characters equal to the title length; "Created: <local date/time string>"; "Last edited: <local date/time string>" (use "unknown" if a timestamp is missing); a blank line; then the note body with line endings normalized. - Separate note blocks with a blank line, a line of 40 "=" characters, and a blank line. End the file with a trailing newline. - Encode the file as UTF-8 with a leading BOM (\ufeff) and use Windows-style CRLF line endings throughout, so it opens correctly in Notepad, TextEdit and similar editors, including accented characters. - Download filename: "notes-YYYY-MM-DD.txt" using the current local date with zero-padded month and day. MIME type text/plain;charset=utf-8. - The download happens entirely in the browser via a temporary Blob link that is clicked programmatically. The temporary URL is released after about 1 second. No network requests. - README: add a bullet saying to click **Download all notes** (at the bottom of the list) to save a copy of every note in a single text file, as a handy backup.

Safety check ✓ Safe

The change only reads the notes already stored in the browser and saves them to a local file through a standard browser download. There are no network requests, new dependencies, or changes to unrelated code. The README edit matches what was asked.

Conversation that created this patch
@theo
Add a button to download all notes as a text file.
AI
I added a **"⬇ Download all notes"** button at the bottom of the notes list. Click it and your browser saves a text file named with today's date, like `notes-2025-01-31.txt`. In the file, notes are listed newest first, the same order as in the app. Each note shows its title, when it was created and last edited, and then its text, with a line of "=" signs between notes. The file should open fine in Notepad, TextEdit or any other text editor, including accents and other special characters. The button is greyed out when you have no notes yet. This also gives you an easy way to back up your notes, since they're otherwise stored only in your browser. I added a line about that to the README. I checked that the code has no errors, but I couldn't click the button in a real browser here, so try one download to make sure it works for you.
Code changes (for the curious, 2 files) · raw
README.md +1 −0
@@ -5,6 +5,7 @@ A simple notes app that runs entirely in your web browser. There is nothing to i
- Your notes are listed on the left. The editor for the selected note is on the right.
- Everything you type is saved automatically.
- You can search your notes, create new ones, and delete ones you no longer need.
+- Click **Download all notes** (at the bottom of the list) to save a copy of every note in a single text file. This is a handy way to back up your notes.
## How to start it
index.html +49 −0
@@ -100,6 +100,13 @@
overflow: hidden;
text-overflow: ellipsis;
}
+ #sidebar-bottom {
+ padding: 10px 12px;
+ border-top: 1px solid var(--border);
+ }
+ #download-btn { width: 100%; }
+ button:disabled { opacity: 0.5; cursor: default; }
+ button:disabled:hover { border-color: var(--border); }
.empty-list {
padding: 20px 14px;
color: var(--muted);
@@ -169,6 +176,9 @@
<button id="new-btn" class="primary" title="New note (Ctrl+Alt+N)">+ New</button>
</div>
<ul id="note-list" aria-label="Notes"></ul>
+ <div id="sidebar-bottom">
+ <button id="download-btn" title="Save a copy of all your notes as a text file">⬇ Download all notes</button>
+ </div>
</aside>
<main id="editor">
@@ -204,6 +214,7 @@
var statusEl = $("status");
var deleteBtn = $("delete-btn");
var placeholderEl = $("placeholder");
+ var downloadBtn = $("download-btn");
// ---------- Storage ----------
@@ -341,6 +352,7 @@
}
function render() {
+ downloadBtn.disabled = notes.length === 0;
renderList();
renderEditor();
}
@@ -377,6 +389,42 @@
save();
}
+ function fullDate(ts) {
+ return ts ? new Date(ts).toLocaleString() : "unknown";
+ }
+
+ function downloadAll() {
+ if (saveTimer) save();
+ if (notes.length === 0) return;
+
+ var divider = "\n\n" + new Array(41).join("=") + "\n\n";
+ var text = sortedNotes().map(function (note) {
+ var title = displayTitle(note);
+ return title + "\n" +
+ new Array(title.length + 1).join("-") + "\n" +
+ "Created: " + fullDate(note.created) + "\n" +
+ "Last edited: " + fullDate(note.updated) + "\n\n" +
+ (note.body || "").replace(/\r\n?/g, "\n");
+ }).join(divider) + "\n";
+
+ // Windows-style line endings so the file opens nicely everywhere;
+ // the leading BOM tells older programs the text is UTF-8.
+ text = "\ufeff" + text.replace(/\n/g, "\r\n");
+
+ var d = new Date();
+ var pad = function (n) { return (n < 10 ? "0" : "") + n; };
+ var fileName = "notes-" + d.getFullYear() + "-" + pad(d.getMonth() + 1) + "-" + pad(d.getDate()) + ".txt";
+
+ var url = URL.createObjectURL(new Blob([text], { type: "text/plain;charset=utf-8" }));
+ var a = document.createElement("a");
+ a.href = url;
+ a.download = fileName;
+ document.body.appendChild(a);
+ a.click();
+ document.body.removeChild(a);
+ setTimeout(function () { URL.revokeObjectURL(url); }, 1000);
+ }
+
function onEdit() {
var note = findNote(selectedId);
if (!note) return;
@@ -392,6 +440,7 @@
$("new-btn").addEventListener("click", newNote);
$("placeholder-new").addEventListener("click", newNote);
deleteBtn.addEventListener("click", deleteNote);
+ downloadBtn.addEventListener("click", downloadAll);
titleEl.addEventListener("input", onEdit);
bodyEl.addEventListener("input", onEdit);
searchEl.addEventListener("input", renderList);