You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: clipper/CHANGELOG.md
+22Lines changed: 22 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,5 +1,27 @@
1
1
# Clipper Plugin - Comprehensive Changelog
2
2
3
+
## Version 2.4.3 (2026-04-22)
4
+
5
+
### Data-loss hotfix: atomic writes for pinned items and notecards
6
+
7
+
Fixes a data-loss bug that could silently destroy pinned items (`pinned.json`) or individual notecards (`notecards/*.json`) under rare timing conditions. A 0-byte ghost file left on disk was traced back to three compounding issues in the save path:
8
+
9
+
1.**Non-atomic shell redirection.** All writes used `base64 -d > "$file"`, which opens the target with `O_TRUNC`**before** decoding and writing. If the process was interrupted between truncate and write (shell restart, kill, OOM, base64 failure), the file was left at 0 bytes with no recovery possible.
10
+
2.**Rename race in `updateNoteCard`.** When a note's title changed, the old file was deleted via a parallel `rm``execDetached` call issued alongside the new `saveNoteCard`. If the `rm` completed but the save failed, the note vanished entirely.
11
+
3.**Empty-base64 edge case.** If `stringToBase64()` returned an empty string (e.g. from a malformed note object), the shell pipeline still truncated the target, producing a guaranteed 0-byte file.
12
+
13
+
**Fix:** new `atomicWriteBase64(filePath, base64, oldFilePath)` helper in `Main.qml`:
14
+
15
+
- Writes to a temp file (`<file>.tmp`) first, verifies it is non-empty (`[ -s "$t" ]`), then atomically renames over the target with `mv -f`.
16
+
- Only removes the old file **after** the new file is safely in place (and only when the old path differs from the new path).
17
+
- Passes `filePath`, `oldFilePath`, and `base64` via argv (not string interpolation) — shell-injection safe and robust against filenames with spaces/quotes.
18
+
- Refuses empty writes up front (`base64.length === 0`) and logs a warning instead of truncating the target.
19
+
-`saveNoteCard` now validates `note.id` and `JSON.stringify` length ≥ 10 before writing.
20
+
21
+
Applied to `savePinnedFile`, `saveNoteCard`, `updateNoteCard`, and `exportNoteCard`.
22
+
23
+
Also: `Component.onCompleted` now sweeps any stale `*.json.tmp` files left over from previous interrupted writes, so a crash mid-hotfix cannot leak temp files indefinitely.
0 commit comments