Ctrl K
ComunidadGuías…

CHANGELOG.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Changelog

All notable changes to Simple Notes Sync will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

**🌍 Languages:** [Deutsch](CHANGELOG.de.md) · **English**

---

## [2.21.0] - 2026-10-25

### ✨ New Features

**Self-Signed Certificates** ([0ce8894](https://github.com/inventory69/simple-notes-sync/commit/0ce8894))
- When Android rejects the server certificate, **Test Connection** in the server settings shows it with issuer, validity and the certificate's SHA-256 fingerprint, a checksum that identifies the certificate. If it matches the one shown on the server, **Trust** accepts exactly this certificate for this server, also when the host name does not match
- Android checks every certificate first. Certificates from Let's Encrypt or an installed CA work as before
- Each server keeps its own accepted certificate. If it changes, the next connection test shows the new one and points out the change
- Certificate errors now have their own messages. The server status shows "Certificate not trusted", and background syncs list the error in the sync status dialog
- The [quick start](QUICKSTART.md#4-self-signed-certificates) describes both options: accepting the certificate in the app, or installing your own CA on the phone. For a CA that renews certificates automatically, such as Caddy or step-ca, the CA is the better option

**Edit Date Only Changes With the Content** ([85ec47c](https://github.com/inventory69/simple-notes-sync/commit/85ec47c))
- Moving a note to another folder, pinning, coloring or archiving it no longer changes the date on the card or its position when sorting by date
- Changes to title, text or checklist items count as edits, in the editor, in widgets and from Markdown files
- Notes from earlier versions keep their current date. The desktop app shows the same date with its next update
- Thanks to Ludo for the report!

**Checkbox Icons in Note Cards and Widgets** ([30e73ca](https://github.com/inventory69/simple-notes-sync/commit/30e73ca))
- Checklist previews in the note list use checkbox icons instead of the ☑️ emoji, checked items are dimmed
- The list widget and the locked note widget use checkbox symbols with dimmed checked items, the list widget also crosses them out
- Thanks to [@lincomax](https://github.com/lincomax) for the request! ([#118](https://github.com/inventory69/simple-notes-sync/issues/118))

### 🐛 Bug Fixes

**Editor Menu With the Keyboard Open** ([5468d15](https://github.com/inventory69/simple-notes-sync/commit/5468d15))
- With the keyboard open, the editor menu was cut off and Archive and Delete were missing. The keyboard now closes before the menu opens
- Thanks to [@lincomax](https://github.com/lincomax) for the report! ([#114](https://github.com/inventory69/simple-notes-sync/issues/114))

---

## [2.20.2] - 2026-10-22

### 🐛 Bug Fixes

**Sync Works Again With Baïkal and Similar Servers** ([38d89a4](https://github.com/inventory69/simple-notes-sync/commit/38d89a4))
- Some WebDAV servers, Baïkal among them, send data in reply to a short status request where none is allowed. The app then misread the next reply on the same connection, and syncing stopped with "Unexpected status line"
- Status requests now get their own connection, which is closed right after the answer. Syncing needs no extra requests, and no idle connection is left open
- Thanks to Dirk for the report and for testing!

---

## [2.20.1] - 2026-10-12

### ⚡ Performance Improvements

**Lower Battery Use While the Phone Sleeps** ([f70be44](https://github.com/inventory69/simple-notes-sync/commit/f70be44))
- The app now closes idle server connections at the end of every sync and when it goes to the background, instead of leaving them to time out
- This helps most with servers that send keepalives, like the recommended hacdias/webdav, and when battery optimization is off for the app: Wi-Fi no longer wakes up for an open connection
- Requests that are still running are not affected, and syncing needs no extra requests

---

## [2.20.0] - 2026-10-11

### ✨ New Features

**Sync Pauses When the Folder Was Encrypted on Another Device** ([6ffc8f5](https://github.com/inventory69/simple-notes-sync/commit/6ffc8f5), [86da522](https://github.com/inventory69/simple-notes-sync/commit/86da522), [ef67630](https://github.com/inventory69/simple-notes-sync/commit/ef67630))
- This prepares for the upcoming optional end-to-end encryption ([#9](https://github.com/inventory69/simple-notes-sync/issues/9)). This version does not encrypt anything yet
- A later version will let a device encrypt the sync folder. A version that cannot read it would keep writing unencrypted notes into the abandoned folder while reporting "synced", and a restore from the server would remove your local notes before finding anything it can read. Updating now means your device stops safely instead
- Every sync now first checks whether the sync folder was encrypted on another device. If so, syncing pauses and nothing on the server is changed: no uploads, downloads or deletions, Markdown files and images included. Your notes stay on the device and your changes are kept. The pause lifts by itself once encryption is turned off again, or when you switch to another folder or server
- Actions that reach the server outside a sync check the same way: restoring from the server, removing a folder from the server, the Markdown sync, "Compare" and "Use server version" on a conflict, the connection test and the import wizard
- The sync status dialog shows "Sync paused" with an explanation and a "Try again" button, the badge counts it, and a single notification tells you when it starts
- The desktop app does the same since v0.15.0. An idle sync now sends one request more (four instead of three)

**Read Mode for Checklists** ([062b8f8](https://github.com/inventory69/simple-notes-sync/commit/062b8f8))
- Checklists can be switched into a read mode with the eye/pencil icon in the toolbar, like text notes. The item texts can't be edited there, drag handles, item menu and the add bar are hidden, and tapping anywhere on a row checks it off
- A new display setting opens existing checklists in read mode (off by default)
- Thanks to [@oliyoup](https://github.com/oliyoup) for the request! ([#156](https://github.com/inventory69/simple-notes-sync/issues/156))

### 🐛 Bug Fixes

**Keep Import Turned Shared Notes Into a List of Email Addresses** ([ed32e5b](https://github.com/inventory69/simple-notes-sync/commit/ed32e5b))
- Google Takeout lists a note's collaborators in its HTML file. When the JSON had no checklist, the import read that list as checklist items, even for text notes. Shared text notes came in as a checklist of the collaborators' email addresses and their text was lost
- The text now comes from the JSON whenever it is there. Imported checklists also drop Keep's bullet character and decode entities like `ä`
- Thanks to [@jpsnyder](https://github.com/jpsnyder) for the report! ([#162](https://github.com/inventory69/simple-notes-sync/issues/162))

### 📚 Documentation

- The docs were rewritten against the current app and are English only now: README, quick start, and four guides for features, sync, backup and troubleshooting. The German README is a short entry point
- The server setup in README and quick start now uses `config.yml`. The old `.env` steps no longer worked

### 🌍 Translations

- **Italian** (100%): [@redenfire](https://github.com/redenfire)
- **Chinese (Simplified)** (100%): [@heretic43](https://github.com/heretic43)
- **Norwegian Bokmål** (100%): [@xdpirate](https://github.com/xdpirate)
- **Russian** (99%): [@disfated](https://github.com/disfated) / Yury Pavlovsky
- **Brazilian Portuguese** grew to 36% thanks to Rafael. It joins the app at 40%

---

## [2.19.0] - 2026-09-29

### ⚠️ Good to Know

- **Encrypted backups use a new format.** A backup created with a password in 2.19.0 can only be restored by 2.19.0 or newer. Unencrypted backups from 2.19.0 still restore in older versions, but without their images. Every older backup can still be restored
- **Markdown files with a name of their own get renamed.** A file you created in a Markdown editor under your own file name is renamed to the note's title after the first sync. Links in Obsidian or similar tools that point to the old name break

### ✨ New Features

**A Sync Status Dialog That Tells You What to Do** ([decdf93](https://github.com/inventory69/simple-notes-sync/commit/decdf93), [af73f00](https://github.com/inventory69/simple-notes-sync/commit/af73f00), [d94557b](https://github.com/inventory69/simple-notes-sync/commit/d94557b))
- The help icon next to the sync status only showed a counter and an icon legend. Conflicts and failed syncs did not appear there, and nothing suggested a next step
- It now opens a sync status dialog: when the last sync succeeded, cards for a failed sync, conflicts and missing files with tappable note titles, a retry button and shortcuts to the server settings, sync settings and activity log. The icon legend stays as a collapsible section
- A short network hiccup no longer looks like an emergency. The dialog says "Can't sync right now" and points out that your notes stay on the device. Only when a sync is failing and none has gone through for more than a day does it switch to a red "Not synced for a while" card, the same rule as the background warning
- The badge on the icon also counts conflicts and a sync that has been failing for more than a day, and every problem notification opens the dialog directly

### 🐛 Bug Fixes

**Backups With Many Images Crashed the App** ([b1bbaee](https://github.com/inventory69/simple-notes-sync/commit/b1bbaee))
- Every image is stored in the backup as Base64, and the whole file was built in memory several times over. On a device with a small heap, 40 images were enough for an out of memory crash that left an empty file behind. Restoring read the whole file into memory the same way
- Backups are now written and read as a stream, one image at a time. Plain backups keep their JSON format. Encrypted backups use a new chunked format (SNE2: AES-256-GCM over 64 KiB chunks, 600,000 PBKDF2 iterations), because AES-GCM on Android buffers the whole plaintext while encrypting
- If a backup still fails, the app shows a message instead of crashing and deletes the empty file
- Thanks to the Google Play reviewer who reported the crash!

**Rotating the Screen Could Turn an Encrypted Backup Into a Plain One** ([d6a7388](https://github.com/inventory69/simple-notes-sync/commit/d6a7388))
- If the device was rotated or dark mode switched while the save dialog was open, the backup screen forgot that "Encrypt Backup" was on and wrote an unencrypted backup without asking for a password
- The choice now survives, and cancelling the password dialog deletes the file the save dialog had already created

**Backup Images Lost Their Names in Release Builds** ([2bd6efb](https://github.com/inventory69/simple-notes-sync/commit/2bd6efb))
- Since 2.12.0, R8 renamed a field in release builds, so the image name was stored under "a" instead of "name". Restoring such a backup in a build with a different mapping brought the note back without its image
- The name is now written as "name" and "a" is still read, so older backups keep their images

**Restore Skips Unsafe Image Names** ([fa216a5](https://github.com/inventory69/simple-notes-sync/commit/fa216a5))
- A crafted backup could use an image name like `../../shared_prefs/x.xml` and make the app create files outside its image folder (existing files were never overwritten). Such names are now skipped with a warning

**Notes Missing on the Server No Longer Empty the App** ([fa2d207](https://github.com/inventory69/simple-notes-sync/commit/fa2d207))
- Deletion detection only stopped when every synced note was missing on the server. With 49 of 50 notes gone, 49 went to the trash and notes already in the trash were deleted for good
- Sync now stops deleting as soon as more than half of the synced notes are missing (from ten notes up) and writes the reason to the activity log. With fewer notes you can still delete them all from the web interface
- Changing the sync folder and restoring from the server now reset the server caches the same way. Restore used to keep the old "last successful sync" time
- This is the groundwork for end-to-end encryption ([#9](https://github.com/inventory69/simple-notes-sync/issues/9))

**An Old Markdown Copy Overwrote Newer Notes** ([0a2ec08](https://github.com/inventory69/simple-notes-sync/commit/0a2ec08))
- With Markdown auto sync on, a note changed only on the server, for example on the desktop, was reverted: the download took the new version, then the Markdown import took the app's own older copy and uploaded it again. A device clock a few seconds behind the server was enough
- Copies the app wrote itself are now recognised by their E-Tag, and a forced import needs a previous sync

**Notes From a Markdown Editor Ended Up in the Trash** ([6488a04](https://github.com/inventory69/simple-notes-sync/commit/6488a04), [660cfb8](https://github.com/inventory69/simple-notes-sync/commit/660cfb8), [1e891b1](https://github.com/inventory69/simple-notes-sync/commit/1e891b1))
- A new .md file created in an editor such as Obsidian became a note in the app, but its JSON never reached the server, so the next sync moved it to the trash. Edits made in the editor never reached other devices either
- Files without a note id got a new id on every import, so each edit, a restore and every further device created a duplicate. The first import now writes the id into the file's frontmatter
- Such a file is renamed to the note's title on the next export, see "Good to Know" above

**Every Rename Left Another Markdown Copy Behind** ([9f58828](https://github.com/inventory69/simple-notes-sync/commit/9f58828))
- The Markdown export named the file after the current title and never remembered the old one. Renames, folder changes and imported editor files left copies of the same note behind, so Markdown editors showed notes two or three times, and editing a stale copy was imported as a change
- The export now remembers each note's file and removes the old one after checking that it really belongs to the note. A one-time cleanup removes existing leftovers, but only in clear cases: files without an id, foreign ids and a note's only copy stay

**Failed Markdown and Image Exports Stayed Invisible** ([be1e08c](https://github.com/inventory69/simple-notes-sync/commit/be1e08c), [51e8072](https://github.com/inventory69/simple-notes-sync/commit/51e8072))
- A Markdown copy or image that failed to upload was only written to the log. The notes were in sync, so every following sync looked clean while the file on the server stayed missing
- Open export problems now show up in the sync banner and the sync status dialog with a readable reason (409, 413 and 507 included), and the next sync retries exactly those files without waiting for an edit
- A background sync shows one quiet warning when problems first appear, and a retry that only transfers missing files says "Missing files transferred" instead of "Nothing to sync"

### 🎨 UI Improvements

**Unchecking Scrolls to the Item, Not to the Top** ([49a08df](https://github.com/inventory69/simple-notes-sync/commit/49a08df))
- Unchecking an item always scrolled the checklist back to the top, even when the item landed far below, so you never saw where it went
- The list now scrolls to the item's new position with the same cut and glide motion. Items near the start still land at the top, and an item already on screen simply grows back in place. The setting is now called "Scroll to unchecked item"

**Notifications Only for Problems by Default** ([3523e48](https://github.com/inventory69/simple-notes-sync/commit/3523e48))
- "Errors and warnings only" is now on by default, so a background sync no longer posts a success notification. Failures, warnings and conflicts still notify
- This applies to every install that never touched the toggle, existing ones included. A choice you made, also one restored from a backup, is kept

**The Back Gesture in Settings Fades Again** ([3e90d23](https://github.com/inventory69/simple-notes-sync/commit/3e90d23))
- Navigation 2.10 animates the predictive back gesture with a scale, which clashed with the fade used everywhere else in settings

### 🔧 Technical Improvements

**Toolchain and Libraries Up to Date** ([7589dcf](https://github.com/inventory69/simple-notes-sync/commit/7589dcf), [3397cf1](https://github.com/inventory69/simple-notes-sync/commit/3397cf1), [c2f7b80](https://github.com/inventory69/simple-notes-sync/commit/c2f7b80), [b904296](https://github.com/inventory69/simple-notes-sync/commit/b904296), [d6cda9c](https://github.com/inventory69/simple-notes-sync/commit/d6cda9c))
- Gradle 9.7.1, Android Gradle Plugin 9.4.1 with built-in Kotlin, Kotlin 2.4.20, Compose BOM 2026.09.00, OkHttp 5.5.0, Glance 1.2.0, Coil 3.6.3 and smaller AndroidX bumps. compileSdk is 37, targetSdk stays 36
- The release APK is 3.8 % smaller than in 2.18.1, and a sync still needs the same number of requests

### 🌍 Translations

- **Chinese (Simplified)** (100%): [@heretic43](https://github.com/heretic43)
- **Russian** (100%): [@disfated](https://github.com/disfated) / Yury Pavlovsky

---

## [2.18.1] - 2026-09-21

### 🐛 Bug Fixes

**One Widget With a Long List Took Every Widget Down** ([3cf8fc6](https://github.com/inventory69/simple-notes-sync/commit/3cf8fc6), [e51b5fe](https://github.com/inventory69/simple-notes-sync/commit/e51b5fe), [9e9f25c](https://github.com/inventory69/simple-notes-sync/commit/9e9f25c), [c113ccc](https://github.com/inventory69/simple-notes-sync/commit/c113ccc))
- A widget whose RemoteViews exceed the launcher's binder buffer does not merely fail to draw. AppWidgetService throws TransactionTooLargeException, logs "Widget host dead" and drops the host callbacks, so every widget on the home screen stops updating until the launcher restarts - for most users that means a reboot
- The item limits sat far above that budget. `SizeMode.Responsive` made it worse by translating the layout once per breakpoint and packing all seven variants into a single RemoteViews, so every row was paid for seven times, and the folder rows above the notes list had no limit at all
- The note widget now renders at its exact size and re-renders on resize instead of shipping six unused layouts. Folders and notes share one budget in the list widget. And because AppWidgetService delivers the RemoteViews of all widgets of a host in one go, through the same 1 MB buffer, the budget itself is shared: `WidgetPayloadBudget` divides 400 KB by the number of placed widgets and derives the row counts from the measured cost per row. A single widget now renders 42 list rows, 85 checklist items or 50 markdown items - more than the old fixed caps - while several widgets together stay inside the buffer
- The numbers come from measurements on a device rather than estimates ([f38aebc](https://github.com/inventory69/simple-notes-sync/commit/f38aebc)): `GlanceAppWidget.compose()` builds the exact RemoteViews that `update()` sends, so `Parcel.dataSize()` on it gives the number the exception actually hangs on. Cross-checked against three real failures, the deviation stays under two percent
- Thanks to [@i-gusarov](https://github.com/i-gusarov) for the report!

**An Image-Heavy Note Could Leave Its Widget Silently Stale** ([2a7857b](https://github.com/inventory69/simple-notes-sync/commit/2a7857b))
- The image budget was documented as protecting the same 1 MB transaction, which it never did: bitmaps travel through shared memory, so eight images produce no large transaction at all. The limit that does apply is AppWidgetService's own, six times screen width times height, and overshooting it throws an IllegalArgumentException that leaves the widget showing old content with no error in sight
- The budget was also checked before decoding, so the last image could exceed it by a full decode. It is now checked after decoding, with the alt text as the fallback, and the limit that actually governs this is recorded next to it

**Links From a Note Landed on the App's Own Task** ([c52dae4](https://github.com/inventory69/simple-notes-sync/commit/c52dae4))
- Compose's default link handler starts the activity without flags, so an app opened from a note link joins the notes app's task whenever it uses the standard launch mode. The launcher then keeps bringing that task to the front: tapping the Simple Notes icon showed the foreign app instead of the notes, until the card was swiped out of Recents
- Links now open in their own task, in the editor as well as in the About and Contributors screens. A link with an unknown scheme such as `foo://` no longer takes the app down either - the default handler threw where the new one falls back quietly
- Thanks to the reporter who mailed in the ARTE TV case!

### 🌍 Translations

- **Norwegian Bokmål** (100%): [@xdpirate](https://github.com/xdpirate)
- **Chinese (Simplified)** (100%): [@heretic43](https://github.com/heretic43)
- **Russian**: [@disfated](https://github.com/disfated) / Yury Pavlovsky - the sort reset string

---

## [2.18.0] - 2026-09-20

### ✨ New Features

**Tables Look Like Tables** ([39530d0](https://github.com/inventory69/simple-notes-sync/commit/39530d0))
- The desktop client writes Markdown tables as pipe rows, and Android had no table block at all, so the same note showed a grid on one device and a wall of pipes on the other. Tables now parse into their own block and render as a real grid in the editor preview: column widths measured from the content, column alignment, inline formatting inside the cells, and horizontal scrolling when the table is wider than the screen
- The PDF export draws the same grid and repeats the header row after a page break. Note cards and both widgets have no room for a grid, so each row is flattened to its cells joined by " · " instead of showing raw pipes. In the raw editor the pipes and the delimiter row are dimmed, so structure reads as structure
- The toolbar has a table button: it inserts a skeleton with visible placeholders, and appends a row when the cursor is already inside a table
- Three deliberate deviations from strict GFM, all following one rule - never swallow what you typed: a delimiter row counts as soon as one cell is a real `---` cell, so typing into it no longer collapses the table into raw text; the column count follows the widest row, so an extra cell adds a column instead of being cut off; a row whose cells are all delimiters is dropped
- Thanks to [@MrsMinchen](https://github.com/MrsMinchen) for the request!

**One Tap Back to the Default Sorting** ([0b513c6](https://github.com/inventory69/simple-notes-sync/commit/0b513c6))
- Getting back to the app default meant remembering that it is "last modified, descending" and clicking it back together, folder by folder
- The sort dialog now has a default button next to Close. It only shows up when the current setting differs from the default, so it is never a dead control, and it resets the folder you are looking at

### 🐛 Bug Fixes

**Markdown Files and Images Never Reached Some Servers** ([c276d4e](https://github.com/inventory69/simple-notes-sync/commit/c276d4e))
- The Markdown folder and the image folder sit next to the sync folder, and their address was built by replacing the folder name inside the sync URL. That replacement hit every match, including the one inside the host name: with a server at `notes.example.com` the Markdown sync went to `notes-md.example.com`, an address that was never configured and usually does not exist
- Everyone whose server name starts with the sync folder name, or whose path contains it (`/notes-archive/`), was affected. Markdown export, Markdown auto import and image attachments all failed silently while the sync still reported success
- Both folders are now built by appending the suffix to the sync folder URL, so neither the host nor the path can be touched again
- Thanks to [@mihanik78189482](https://github.com/mihanik78189482) for the report and the detailed logs!

**Exported Logs Could Still Contain Your Server Domain** ([3d51325](https://github.com/inventory69/simple-notes-sync/commit/3d51325))
- The log export replaced the host name from the settings. The made up address from the bug above was a different string, so the real domain stayed in the file and ended up in a public issue
- Every host in a URL is replaced now, no matter which address the app actually used
- The redaction is limited to http and https addresses ([812557f](https://github.com/inventory69/simple-notes-sync/commit/812557f)). It used to swallow the content URIs of the backup and import paths as well, and a provider authority identifies nobody while being exactly what tells a restore report apart from a sync one

**A Single Broken Note No Longer Fails the Whole Restore** ([61c6ae3](https://github.com/inventory69/simple-notes-sync/commit/61c6ae3))
- One unusable note made the validation reject the entire backup, so a restore gave back zero of N notes. Backups written before note types existed failed hardest: the missing field arrived as null behind a non-null type and the whole file was reported as corrupt. Nothing was written to the log either, so the restore did nothing and there was nothing to report
- Unreadable notes are skipped and counted now, a restore only fails when no note survives at all, and the reason is logged
- That check runs before the app settings block, so a failed restore can no longer leave your server URL, credentials and sync folder replaced

**Backup and Restore Now Say What Happened** ([bd0804d](https://github.com/inventory69/simple-notes-sync/commit/bd0804d))
- The outcome appeared in a status line that cleared itself after two to three seconds, and the reason for a failure never reached the screen at all. For the one operation that decides whether you get your notes back, that is too little information for too short a time
- The result now stays in a card until you leave the backup screen: note counts on success, the reason on failure. It survives a rotation
- The restore file picker accepts octet-stream and text/plain as well, the same list the import screen already uses. Not every provider reports application/json for a .json file, and a greyed out backup file is indistinguishable from a broken one

**A Backup File in the Import Wizard Became One Empty Note** ([91f7948](https://github.com/inventory69/simple-notes-sync/commit/91f7948))
- Picking a backup under "select files from device" sent it to the generic JSON parser. It found no title and no content at the top level, took the file name as the title and dropped the notes array: a backup holding 19 notes was imported as a single empty note and reported as "1 imported, 0 failed"
- Backup files are recognised now and fail with a pointer to backup and restore. The generic "the file may be corrupted" hint is hidden when a concrete reason is shown below it, because it contradicts it

**Folders Ignored the Sort Order** ([c5d3b66](https://github.com/inventory69/simple-notes-sync/commit/c5d3b66))
- The folder section of the root view was always rendered in fixed alphabetical order, ignoring the sort option and the direction entirely, so flipping the direction moved every note but left the folders untouched
- Folders carry neither a timestamp nor a note type, so those options fall back to the name (as file managers do) while colour follows the palette order. The direction always applies, so the toggle visibly turns the folders too

### 🌍 Translations

- **Norwegian Bokmål** (100%): [@xdpirate](https://github.com/xdpirate)
- **Russian** (100%): [@disfated](https://github.com/disfated) / Yury Pavlovsky
- **Chinese (Simplified)** (100%): [@heretic43](https://github.com/heretic43)

---

## [2.17.0] - 2026-09-11

### ✨ New Features

**Edit a Note Straight From the Home Screen Widget** ([41b4a5c](https://github.com/inventory69/simple-notes-sync/commit/41b4a5c))
- Tapping a text note in the widget opens a small edit card over the home screen and saves the change in place - no detour through the app
- Checklist widgets get a "+" button that appends a new item the same way
- The full editor stays reachable through the widget's options bar
- Android widgets cannot hold a text field, so the card is a transparent screen of the app itself. It needs no "display over other apps" permission

**Images Side by Side** ([6982982](https://github.com/inventory69/simple-notes-sync/commit/6982982), [808ec94](https://github.com/inventory69/simple-notes-sync/commit/808ec94))
- Images that follow each other without a blank line now share one row instead of stacking. A blank line, text in between, or sizes adding up to more than 100% start a new row
- The same rule applies in the editor preview, the note cards, the PDF export and both widgets, so a note looks the same everywhere
- Widgets are no longer capped at three images: pictures are scaled down precisely, and a size budget replaces the fixed limit

### 🐛 Bug Fixes

**"Automatically Every X Minutes" Never Synced** ([df1fd9e](https://github.com/inventory69/simple-notes-sync/commit/df1fd9e), [227b0ec](https://github.com/inventory69/simple-notes-sync/commit/227b0ec))
- The option could be switched on, but periodic syncs also required an old auto-sync switch that lost its place in the settings back in 1.6.0. On every install that had not carried the old value over, nothing was ever scheduled
- If you enabled the option, background syncs now run at the chosen interval - for many of you, for the first time
- The 24-hour "not synced" warning checked the same switch and stayed silent as well. The retired switch is gone; backups that still contain it restore unchanged

**After a Phone Switch, Sync Stopped Without a Word** ([d751e49](https://github.com/inventory69/simple-notes-sync/commit/d751e49), [2ebb992](https://github.com/inventory69/simple-notes-sync/commit/2ebb992))
- Credentials are not carried over to a new device. A server without them counted as "nothing to sync", so the sync reported success while nothing happened
- It now ends in the error banner, and background syncs show the error notification. The 24-hour warning names the missing credentials instead of blaming an unreachable server
- The server settings show a hint while a server is configured but the username or password is empty

**Your Password Could End Up in a Backup** ([fe39a52](https://github.com/inventory69/simple-notes-sync/commit/fe39a52), [c633ce7](https://github.com/inventory69/simple-notes-sync/commit/c633ce7))
- When the device's key store was unusable, username and password were written unencrypted into the settings file - the same file that goes into cloud backups and device transfers
- The unencrypted fallback now lives in its own file, and both credential files are excluded from backups and transfers
- If credentials can only be stored unencrypted, the server settings say so with a snackbar and a warning card that stays until encryption works again. A backup restore goes through the same check

### 🔧 Technical Improvements

**Credentials Encrypted With the Android Keystore Directly** ([0bd4632](https://github.com/inventory69/simple-notes-sync/commit/0bd4632))
- `androidx.security:security-crypto` has been deprecated since 2025. Credentials are now encrypted with an AES-256-GCM key that never leaves the device's key store
- Existing credentials move over automatically; nothing is deleted while the key store is unavailable
- The old library stays only as a migration source and goes away in 2.20.0

### 🌍 Translations

- **Chinese (Simplified)** (100%): [@heretic43](https://github.com/heretic43) - the word counter settings from 2.16.1
- **Russian** (94%): [@disfated](https://github.com/disfated) / Yury Pavlovsky - the conflict resolution and note stats strings from 2.16.0, plus revised sync legend texts

---

## [2.16.1] - 2026-09-09

### 🐛 Bug Fixes

**The Word Counter Now Gets Out of the Way** ([9e1c611](https://github.com/inventory69/simple-notes-sync/commit/9e1c611))
- The counter shipped in 2.16.0 as a fixed row under the note. It cost a 48 dp line of editing space on every note, it could not be turned off, and it forgot the chosen unit the moment the editor closed
- It is an overlay now: the pill floats over the bottom-right corner of the content, and the row's height goes back to the text
- Tapping cycles words, characters, hidden - and the mode is remembered across notes, so a pill tapped away collapses to a small chevron instead of returning on the next note
- Display settings gain a three-way choice: always, only in read mode, or off. Turning it back on resets a tapped-away pill, or switching it on would reveal nothing but the chevron
- Checklists no longer show the counter: a floating pill overlaps the item rows and their drag handles, and the fixed row it used to sit in is gone
- Follow-up to the counter requested in [#126](https://github.com/inventory69/simple-notes-sync/issues/126)

### 🌍 Translations

- **Chinese (Simplified)** (100%): [@heretic43](https://github.com/heretic43) - the conflict resolution, note stats and cross-folder search strings from 2.16.0

---

## [2.16.0] - 2026-09-07

### ✨ New Features

**Pick Which Version Survives a Sync Conflict** ([5ad38e3](https://github.com/inventory69/simple-notes-sync/commit/5ad38e3), [803c6de](https://github.com/inventory69/simple-notes-sync/commit/803c6de))
- A note held back by a conflict used to be a dead end: it was never uploaded, and editing it again only led back into the same conflict. The editor now opens with a banner offering the two ways out - keep yours, or take the server's
- "Compare" fetches the version currently on the server and puts it next to yours, because "use the server version" writes immediately and should not be a guess
- The sync banner counts conflicting *notes*, not conflict events - one note detected twice in a single run is one conflict, not two

**A Word and Character Count Below the Note** ([0bb5120](https://github.com/inventory69/simple-notes-sync/commit/0bb5120), [e8a4805](https://github.com/inventory69/simple-notes-sync/commit/e8a4805))
- Tap it to switch between words and characters. It stays visible in the Markdown preview, where the question "how long is this" is just as reasonable as in the editor ([6d8bd92](https://github.com/inventory69/simple-notes-sync/commit/6d8bd92))
- Thanks to [@annny001](https://github.com/annny001) for the request! (#126)

**Widget Settings Moved Into the Plus Menu** ([f67a776](https://github.com/inventory69/simple-notes-sync/commit/f67a776))
- The notes list widget had a gear permanently parked in its corner, taking space from the notes. It now sits in the expanded plus menu, next to "text note" and "checklist", and the menu closes itself when you open the settings
- The header lost the row height the gear used to give it, so the title sat cramped against the top edge. It has its own spacing now ([0858e13](https://github.com/inventory69/simple-notes-sync/commit/0858e13))
- The "hide widget title" setting still promised that the gear would float in the top corner instead. That gear no longer exists - the description now says what actually happens
- Thanks to [@lincomax](https://github.com/lincomax) for the request! (#119)

### 🐛 Bug Fixes

**A Note Edited on Two Devices Was Silently Overwritten** ([0856be9](https://github.com/inventory69/simple-notes-sync/commit/0856be9))
- Whoever synced second won. The upload went out blind, so a change made on another device was overwritten before the download ever got to see it - and the note that lost was gone without a trace
- Before anything is written, the sync now compares the server's state against what this device last saw. If the server has moved on, the note is marked as a conflict and **nothing is uploaded**. Every case that cannot be decided safely - no cached state, a folder listing that failed - lets the upload through as before: a blocked upload would be worse than the missing guard
- The check deliberately does not rely on `If-Match`. The WebDAV server recommended in the setup guide does not evaluate write preconditions at all and answers a wrong one with `201`, so the comparison has to happen in the app, with data every server provides
- A note already marked as a conflict is no longer overwritten by the next download either. Both branches previously looked only for pending edits, so a marked note fell through and was quietly replaced on the following sync

**A Sync That Ended in a Conflict Said "Nothing to Sync"** ([1547b89](https://github.com/inventory69/simple-notes-sync/commit/1547b89))
- Conflicts appeared in no branch of the completion banner, so the one run that actually needed a decision reported the opposite of the truth

**Search Only Looked in the Folder You Were Standing In** ([328c7a6](https://github.com/inventory69/simple-notes-sync/commit/328c7a6))
- Searching from inside a folder hid every match outside it, which is the opposite of what a search is for. Search now spans all folders and each result says which folder it lives in; folder tiles step aside while a search is running, so "select all" no longer picks them up
- Thanks to [@Haagel-FR](https://github.com/Haagel-FR) for the report! (#141)

**Blank Lines Vanished From the Preview** ([9623c98](https://github.com/inventory69/simple-notes-sync/commit/9623c98))
- Markdown collapses consecutive blank lines into a single paragraph break. In a notes app that is wrong: pressing enter three times means you want a gap, not a paragraph boundary. The first blank line stays the separator, every further one is kept
- Thanks to [@J4CKED](https://github.com/J4CKED) for the request! (#140)

### 🌍 Translations

- **Spanish** (52%): [@Kiimby](https://github.com/Kiimby) / Kimby - the section reorder strings and the restore counter
- **Polish** (100%): [@ldvk0](https://github.com/ldvk0)
- **Norwegian Bokmål** (100%): [@xdpirate](https://github.com/xdpirate)

---

## [2.15.0] - 2026-09-03

### ✨ New Features

**Widgets Grow As Large As the Home Screen Allows** ([1a67c3e](https://github.com/inventory69/simple-notes-sync/commit/1a67c3e))
- Both widgets carried a 530dp resize ceiling, so on a tablet or an unfolded foldable they stopped growing long before the screen ran out. The cap is gone - drag them as far as the launcher permits
- Thanks to [@Wuts0n](https://github.com/Wuts0n) for the pull request! (#136)

**Polish** ([8339409](https://github.com/inventory69/simple-notes-sync/commit/8339409))
- Polish reached full coverage on Weblate but was still being stripped out of the APK and was missing from the language picker. It ships now, and it is selectable
- The coverage comments in `localeFilters` and `locales_config.xml` still described the v2.12.0 state and never mentioned pt-rBR and nl; both are current again

### 🐛 Bug Fixes

**Saving an Unchanged Note Moved It to the Top of the List** ([78801c6](https://github.com/inventory69/simple-notes-sync/commit/78801c6))
- The save button always ran the full save, stamping a new modification date even when nothing had been typed. Opening a note, reading it, and pressing save was enough to push it to the front of a list sorted by date - and to upload it again on the next sync
- The button now checks the same dirty flag the back gesture has always checked. Sync and navigation still run, so a pending change written by autosave is still uploaded when you press save
- Thanks to [@JavaRaf](https://github.com/JavaRaf) for the report! (#124)

**Credentials Did Not Survive a Device Transfer** ([fac2414](https://github.com/inventory69/simple-notes-sync/commit/fac2414))
- A device transfer copies the encrypted credential file but not the hardware key that opens it - that key cannot leave the device it was made on. The restored file was permanently unreadable, and because only successes were remembered, every credential access tried to open it again: eight failed attempts per sync, taking 40-69% of the total sync time, with the password quietly falling back to unencrypted storage
- The app now recognises the dead file, discards it once, and builds a fresh one. The file is also excluded from backup and device transfer so it cannot come back. On a real install this took the failures from 53 to 0 and the sync median from 398 ms to 84.5 ms
- Only a decryption failure discards the store. A transient problem such as a full disk gives up for that session and retries on the next start, because after migration the encrypted store holds the only copy of the password

### 🌍 Translations

- **Polish** (100%): [@ldvk0](https://github.com/ldvk0) - a complete new locale
- **Chinese (Simplified)** (100%): [@heretic43](https://github.com/heretic43)
- **Norwegian Bokmål** (100%): [@xdpirate](https://github.com/xdpirate)
- **Indonesian** (100%): [@arifpedia](https://github.com/arifpedia) / Arif Budiman

---

## [2.14.0] - 2026-08-08

### ✨ New Features

**An Activity Log That Never Leaves the Device** ([ae27ff2](https://github.com/inventory69/simple-notes-sync/commit/ae27ff2), [40fd353](https://github.com/inventory69/simple-notes-sync/commit/40fd353), [cf449ae](https://github.com/inventory69/simple-notes-sync/commit/cf449ae), [fd15fbe](https://github.com/inventory69/simple-notes-sync/commit/fd15fbe))
- When a note went missing there was no way to find out what happened to it. Settings now has an activity log: created, edited, moved, trashed, restored, and every sync with the reason it started - a save, the widget, a pull-to-refresh, the periodic job. It is written locally, never uploaded, and only leaves the device if you share it yourself
- Each row says where the action came from, in text and not only as an icon ([fac13a0](https://github.com/inventory69/simple-notes-sync/commit/fac13a0), [96716d4](https://github.com/inventory69/simple-notes-sync/commit/96716d4))

**Images: A Default Size, and Copy From the Long-Press Menu** ([3dbd400](https://github.com/inventory69/simple-notes-sync/commit/3dbd400), [8f5ef72](https://github.com/inventory69/simple-notes-sync/commit/8f5ef72))
- Newly inserted images took the full width and had to be resized by hand every time. Display settings now hold a default size that new images start at
- The image long-press menu can copy the image, next to the actions it already had

**Digest Authentication** ([bd009ec](https://github.com/inventory69/simple-notes-sync/commit/bd009ec))
- WebDAV servers that do not offer basic authentication now work. Both methods are negotiated automatically - nothing to configure

### 🔒 Privacy

**Logs Get Scrubbed Before They Leave the Device** ([0d7ce77](https://github.com/inventory69/simple-notes-sync/commit/0d7ce77), [7ec7eff](https://github.com/inventory69/simple-notes-sync/commit/7ec7eff), [7645954](https://github.com/inventory69/simple-notes-sync/commit/7645954), [e7ff37e](https://github.com/inventory69/simple-notes-sync/commit/e7ff37e), [a333dfb](https://github.com/inventory69/simple-notes-sync/commit/a333dfb))
- A debug log is the fastest way to get a sync problem fixed and the easiest way to publish more than you meant to. The copy the share button hands out now has the server host and path, the username, note titles, folder names and markdown filenames replaced with placeholders. The file on the device stays complete
- Note UUIDs deliberately stay: they identify nobody and are the only thread that ties a problem together across log lines
- Some servers put the account holder's legal name in the WebDAV path. That name is in no title or folder list, so it survived until the path itself started being replaced - in all three spellings it can appear in
- Replacements respect word boundaries, so a folder named "Auto" no longer turns "Automatischer Upload" into placeholder soup

**The Sync Log Stopped Reading the Neighbours' Mail** ([4088e3d](https://github.com/inventory69/simple-notes-sync/commit/4088e3d))
- A one-time cleanup scan listed every entry of the directory *above* the sync folders into the log - other people's folder names and document titles that have nothing to do with this app, and that no anonymizer can recognise. Only the count is logged now
- Thanks to David Jany for spotting this in a beta log

### 🐛 Bug Fixes

**A Folder That Would Not List Looked Like a Deletion** ([9569173](https://github.com/inventory69/simple-notes-sync/commit/9569173))
- When the server failed to list a folder, the notes inside it were absent from the listing, and absent reads as deleted. Notes sat untouched on the server and went to the trash locally. Deletion detection now refuses to run on an incomplete listing, and notes that were already marked wrongly heal on the next sync
- Thanks to [@daalja](https://github.com/daalja) for the report! (#128)

**Folder Names With Spaces Fell Out of the Server Listing** ([68f87d3](https://github.com/inventory69/simple-notes-sync/commit/68f87d3))
- A raw space in the configured server URL produced a request the server answered with an error, and the sync died on it. The URL is canonicalized once now, so what goes on the wire is encoded no matter how it was typed

**Sharing or Copying a Note Lost Its Images** ([46ccccf](https://github.com/inventory69/simple-notes-sync/commit/46ccccf))
- Shared and copied note text dropped the image references, so the receiving app got a note with holes in it

**Cleaning Up a Folder Tried to Delete Directories That Were Not There** ([dda9ab2](https://github.com/inventory69/simple-notes-sync/commit/dda9ab2))
- After the last note left a folder, the app removes the now empty directory from the server. A directory the server did not have counted as "empty" and got a delete request that could only fail - once per cleanup, each one a wasted round trip and a warning in the log that looked like a real problem
- Thanks to [@xmichelf](https://github.com/xmichelf) for the beta log that surfaced this

**Smaller Ones**
- A missing `deletions.json` or `folders.json` is logged as a normal event instead of a warning - on a sync folder where nothing has been deleted yet, the 404 is the expected answer, and it was drowning the warnings that meant something ([2b94d28](https://github.com/inventory69/simple-notes-sync/commit/2b94d28))
- Debug logs show the end of an E-Tag rather than its beginning. Some servers give every file the same leading characters, which made all notes look identical in the log ([fc3b980](https://github.com/inventory69/simple-notes-sync/commit/fc3b980))
- A deep directory listing that returned no nested entries is no longer taken at face value ([48ffa8d](https://github.com/inventory69/simple-notes-sync/commit/48ffa8d))
- JSON files on the server that are not notes stayed out of the note ID set ([1fa738f](https://github.com/inventory69/simple-notes-sync/commit/1fa738f))
- The retry counter counted runs, not retries ([d2ca5ae](https://github.com/inventory69/simple-notes-sync/commit/d2ca5ae))
- Sync outcomes are recorded for every caller, not only the background worker ([3b4cf32](https://github.com/inventory69/simple-notes-sync/commit/3b4cf32))
- The debug log survives a process restart and is only trimmed once it has actually grown ([399c1c5](https://github.com/inventory69/simple-notes-sync/commit/399c1c5), [38d97d0](https://github.com/inventory69/simple-notes-sync/commit/38d97d0))

### ⚡ Performance

**Sync Stopped Asking Questions It Already Knew the Answer To** ([ef54a2b](https://github.com/inventory69/simple-notes-sync/commit/ef54a2b), [6493018](https://github.com/inventory69/simple-notes-sync/commit/6493018), [17d5218](https://github.com/inventory69/simple-notes-sync/commit/17d5218), [79d1ec6](https://github.com/inventory69/simple-notes-sync/commit/79d1ec6), [20a86e1](https://github.com/inventory69/simple-notes-sync/commit/20a86e1))
- Every request to a WebDAV server costs real time - measured against Nextcloud behind Cloudflare, about half a second each. A sync with nothing to do was spending ten to twelve of them. It now needs about two, and a measured sync went from 6.5 to 4.0 seconds
- The app no longer signs in again for every single sync. One sign-in per app start is enough, and it is repeated only when credentials change
- Directory checks, folder listings and the deletion ledger are remembered between syncs instead of being re-asked every time. Deleting notes used to rewrite the shared ledger once per note; it is now a single read and a single write
- One check ran on every sync that could never answer anything but "yes, look at the server" - it was removed along with the requests it made
- With markdown export or images turned off, the app no longer touches those folders at all. With them on, files just exported are no longer downloaded again to read back their ID
- Folders are fetched in one pass instead of one request per folder, where the server supports it
- Moving a note into a folder, or renaming a folder, no longer downloads the note straight back. The move uploads it to the new path and then cleans up the old one, and that cleanup was throwing away what the upload had just learned. Renaming a folder cost one pointless download per note in it ([dba25cf](https://github.com/inventory69/simple-notes-sync/commit/dba25cf))
- Folder cleanup no longer reaches for the markdown mirror when markdown is switched off ([9924cf4](https://github.com/inventory69/simple-notes-sync/commit/9924cf4))

### 🔧 Technical Improvements

**Own WebDAV Client Instead of an Unmaintained Library** ([bd009ec](https://github.com/inventory69/simple-notes-sync/commit/bd009ec))
- Sync ran on `sardine-android`, which has had no updates in years. It is replaced by a small client built directly on OkHttp, covering exactly what the app uses: PROPFIND, GET, PUT, DELETE, MKCOL. Nothing more
- All server workarounds were carried over unchanged - Jianguoyun answering 403, bewCloud rejecting HEAD, the MKCOL fallback. They are now covered by tests against a mock server, which the old setup could not do
- The app got smaller: the dependency and its ProGuard keep rules are gone
- **If anything syncs differently than it did before, please open an issue.** This release swaps out the piece of the app that talks to your server, and no test suite covers every WebDAV server out there

**Stricter Code Quality Gate** ([61ea842](https://github.com/inventory69/simple-notes-sync/commit/61ea842))
- The complexity limits had been raised over time to let existing code through, which made them useless for new code. They are back at their target values; the places that still exceed them are marked individually and get cleaned up release by release

**A Build Type for Testers** ([6f1c616](https://github.com/inventory69/simple-notes-sync/commit/6f1c616), [572b267](https://github.com/inventory69/simple-notes-sync/commit/572b267), [25b27a8](https://github.com/inventory69/simple-notes-sync/commit/25b27a8), [1fc02a9](https://github.com/inventory69/simple-notes-sync/commit/1fc02a9))
- Beta builds are identical to release builds - same signing key, same R8 - and only turn on file logging, the sync debug log and the developer options, so a tester can hand over a log without hunting for a hidden switch. Tags carrying a suffix publish a pre-release and never reach Play or F-Droid; superseded pre-releases are cleaned up automatically
- Thanks to everyone who tested the betas of this release and sent in logs

### 🌍 Translations

- **Chinese (Simplified)** (99%): [@heretic43](https://github.com/heretic43)
- **Indonesian** (95%): Arif Budiman
- **Norwegian Bokmål** (95%): [@xdpirate](https://github.com/xdpirate)
- **Russian** (95%): PONYATIN
- **Italian** (91%): GVE
- **French** (85%): Fred
- Dutch and Brazilian Portuguese were started this release - thanks to Quinten and Rafael. They are not shipped in the app yet: a language joins the system language picker at 40% translated, so a user picking it does not land in a mostly English interface. Both are already open on [Hosted Weblate](https://hosted.weblate.org/projects/simple-notes-sync/android-app/) if you want to help them over the line

---

## [2.13.1] - 2026-07-25

### 🐛 Bug Fixes

**Archive and Delete Missing from the Editor Menu** ([b058088](https://github.com/inventory69/simple-notes-sync/commit/b058088))
- The overflow menu never read its state where Compose could see it, so the archive and delete section was gated on a stale snapshot and stayed hidden until the menu was closed and reopened. On brand-new notes it was missing outright, since the note only exists on disk after the first autosave. Both items are now always shown; deleting a note that was never saved discards it instead of moving it to the trash, and archiving saves it first
- Thanks to [@lincomax](https://github.com/lincomax) for the report! (#114)

**Leaving a Widget-Opened Note Landed in the Wrong Place** ([6fa7257](https://github.com/inventory69/simple-notes-sync/commit/6fa7257))
- Both ways out of the editor did the same thing, so where you ended up depended on what was still open in Recents: the arrow in the toolbar could drop you into a different note, and the system back gesture could bring the editor back instead of returning to the home screen. The arrow now always opens the notes list, and back always leaves the app. Notes opened in-app or via share are unaffected
- Thanks to [@lincomax](https://github.com/lincomax) for the request! (#117)

**"Show Note Icon" Ignored by the Notes List Widget** ([f24909b](https://github.com/inventory69/simple-notes-sync/commit/f24909b))
- The display setting only reached in-app note cards. The widget read its own Glance state and kept showing the type icon and pin badge even when the setting was off. It now reads the app setting and refreshes the widgets as soon as the toggle flips
- Thanks to [@lincomax](https://github.com/lincomax) for the request! (#120)

### 🎨 UI Improvements

**One Card Layout Across All Settings Screens** ([2f15ce6](https://github.com/inventory69/simple-notes-sync/commit/2f15ce6), [01382d2](https://github.com/inventory69/simple-notes-sync/commit/01382d2))
- Import, Backup, Debug, Security, Language and About still used the old flat layout while Display and Sync had moved to section cards. All of them now use the same pattern, dividers are gone, and the seven near-identical chip rows collapse into one shared component. Section headers are centered like the ones in the notes list. The server settings screen is unchanged and follows separately

### 🌍 Translations

- **Chinese (Simplified)** (100%): [@heretic43](https://github.com/heretic43) — all 15 new strings from v2.13.0 translated within days of the release

---

## [2.13.0] - 2026-07-25

### ✨ New Features

**Grouped Settings** ([0a859f0](https://github.com/inventory69/simple-notes-sync/commit/0a859f0), [114ddf3](https://github.com/inventory69/simple-notes-sync/commit/114ddf3), [a22e683](https://github.com/inventory69/simple-notes-sync/commit/a22e683), [3fcdc21](https://github.com/inventory69/simple-notes-sync/commit/3fcdc21))
- Display settings had grown to 15 option groups in one flat scroll, with section headers no longer matching their content. They now sit in four cards: Appearance, Note list, Editor and Image compression. Each card collects its own state, so a single toggle no longer recomposes the whole screen
- The markdown settings screen held one toggle, a folder row and a sync button — too little for its own screen and main-menu card. It becomes the fourth section of the sync screen, whose three existing sections turn into cards as well
- The settings overview groups its ten cards under App, Synchronization, Data and Info headers

**Title-Only Note Cards** ([726b0ab](https://github.com/inventory69/simple-notes-sync/commit/726b0ab), [7cc17ff](https://github.com/inventory69/simple-notes-sync/commit/7cc17ff))
- A new preview-length tier shows just the title in List and Grid view, with a matching option in the notes list widget. Untitled notes keep their first content line in the title slot instead of rendering an empty card
- Thanks to [@iergun71](https://github.com/iergun71) for the request (discussion #110)!

**Hide Timestamp and Note Icon** ([46c88e7](https://github.com/inventory69/simple-notes-sync/commit/46c88e7))
- Two toggles remove the timestamp footer and the note type icon from list, compact and grid cards. With the footer gone, the sync-status icon relocates inline to the end of the last preview line instead of vanishing behind the ellipsis
- Thanks to [@iu0v1](https://github.com/iu0v1) for the report! (#100)

**Optional Scroll-to-Top on Un-Check** ([61c9fb6](https://github.com/inventory69/simple-notes-sync/commit/61c9fb6))
- Un-checking an item scrolled the checklist back to the top, which fights workflows like re-activating items on a grocery list. A switch in Display settings turns it off; the default is unchanged
- Thanks to [@abacus-tabletop-games](https://github.com/abacus-tabletop-games) for the report! (#112)

**Auto-Shrinking Grid Titles** ([de05dbf](https://github.com/inventory69/simple-notes-sync/commit/de05dbf))
- Long titles in the grid shrink to 80% before falling back to ellipsis, and titles and preview text hyphenate so long unbroken words wrap instead of jumping to the next line early

### 🐛 Bug Fixes

**Checkboxes Vanished Below a Bullet** ([40d4d7c](https://github.com/inventory69/simple-notes-sync/commit/40d4d7c))
- A plain bullet directly above task lines swallowed the whole checklist and rendered it as bullets with literal `[ ]` / `[x]` — exactly the list the toolbar's checklist button produces. Dates back to v1.9.0 and was visible in preview, note cards, widgets and PDF export, since all four share the parser. The task regex now also accepts `*`/`+` markers, empty brackets and missing item text, and is shared by preview, live styling and tap-to-toggle

**Checklist Un-Check Animation** ([bfd6868](https://github.com/inventory69/simple-notes-sync/commit/bfd6868))
- Un-checks that reorder across the separator left a ghost row at the viewport edge and pushed neighbours apart mid-scroll. The row now owns its exit animation and collapses in place before the model commit, and the scroll-to-top is a single pixel-exact glide instead of a visible two-phase jump. Covered by frame-exact instrumented regression tests

**Widget Images Ignored the Size Preset** ([394f40e](https://github.com/inventory69/simple-notes-sync/commit/394f40e))
- The four size presets had no effect in the note widget; every image rendered at a fixed height. The 50% default still maps to the previous size, so existing notes look unchanged

**Notes List Widget Showed Stale Notes** ([6984c5a](https://github.com/inventory69/simple-notes-sync/commit/6984c5a))
- The widget loaded its notes outside the recomposed lambda, so the list stayed frozen for the whole Glance session — edits made after opening a note from the widget never showed up

**Missing Image Emoji in Widget Previews** ([f0e42bc](https://github.com/inventory69/simple-notes-sync/commit/f0e42bc))
- Notes with images showed only their (often empty) alt text in the notes list widget instead of the 🖼 indicator the app's note cards use

---

## [2.12.1] - 2026-07-17

### 🐛 Bug Fixes

**Text Widgets Showed an Error** ([503dc2b](https://github.com/inventory69/simple-notes-sync/commit/503dc2b))
- Every text-note widget rendered Glance's error layout ("Content cannot be displayed") instead of the note, in all sizes. The widget's Markdown view read the Compose-UI `LocalContext`, which a Glance composition never provides. Checklist widgets were unaffected. A detekt rule now blocks the wrong import from returning via autocomplete

**Bold + Italic Markdown** ([052d9eb](https://github.com/inventory69/simple-notes-sync/commit/052d9eb))
- `***text***` and `___text___` never rendered as bold italic, and a stray `***` shifted marker pairing for every following inline tag on the same line, breaking the whole line. Fixed in the viewer, widget, HTML export and editor live preview

### 🌍 Translations

- **Chinese (Simplified)** (100%): [@heretic43](https://github.com/heretic43)

---

## [2.12.0] - 2026-07-14

### ✨ New Features

**Image Attachments in Notes** ([1f88777](https://github.com/inventory69/simple-notes-sync/commit/1f88777), [c67725a](https://github.com/inventory69/simple-notes-sync/commit/c67725a), [c7ce100](https://github.com/inventory69/simple-notes-sync/commit/c7ce100))
- Insert images from the picker or paste them straight from the clipboard; they render inline in the editor and preview, backed by a content-addressed asset store with a compression pipeline

**Rich HTML Paste** ([c7ce100](https://github.com/inventory69/simple-notes-sync/commit/c7ce100))
- Pasting from a web page now keeps its formatting: headings, lists, links and tables are converted to Markdown instead of dropping to plain text. The size cap was raised from 200 KB to 8 MB and the conversion runs off the main thread, so large pages paste cleanly without freezing the editor
- Thanks to [@infosundtips](https://github.com/infosundtips) for suggesting the feature and testing it!

**Image Sizing, Alignment & Viewer** ([b91b522](https://github.com/inventory69/simple-notes-sync/commit/b91b522), [9a02592](https://github.com/inventory69/simple-notes-sync/commit/9a02592))
- Resize and align images from a menu, open them full-screen, and inspect EXIF metadata in an info dialog; size and alignment are stored as Markdown tokens

**Image Sync over WebDAV** ([7461ddb](https://github.com/inventory69/simple-notes-sync/commit/7461ddb), [927d06b](https://github.com/inventory69/simple-notes-sync/commit/927d06b))
- Image assets sync alongside notes with garbage collection for orphans, and asset links are translated between the JSON and Markdown-mirror formats

**Images Everywhere Else** ([3672f53](https://github.com/inventory69/simple-notes-sync/commit/3672f53), [a839e2a](https://github.com/inventory69/simple-notes-sync/commit/a839e2a), [f8186d9](https://github.com/inventory69/simple-notes-sync/commit/f8186d9), [d528001](https://github.com/inventory69/simple-notes-sync/commit/d528001))
- PDF export renders attachments honoring size and alignment tokens, widgets show real image bitmaps, and backups include the attachments

**Image Compression Setting** ([f36877d](https://github.com/inventory69/simple-notes-sync/commit/f36877d))
- Choose how aggressively inserted images are compressed

**Preview Mode by Default** ([af1e1c3](https://github.com/inventory69/simple-notes-sync/commit/af1e1c3))
- Text notes can open directly in preview mode instead of the editor

**Calendar-Parsing Strategies** ([c27db7f](https://github.com/inventory69/simple-notes-sync/commit/c27db7f), [c3fb84d](https://github.com/inventory69/simple-notes-sync/commit/c3fb84d), [5348771](https://github.com/inventory69/simple-notes-sync/commit/5348771))
- Calendar export uses the parsed title, location and attendees, and the parsing strategy chosen in the experiment screen is persisted

**Native Image Sharing** ([9f0b1cd](https://github.com/inventory69/simple-notes-sync/commit/9f0b1cd))
- Sharing a note with images attaches them natively instead of sharing raw text

### ⚡ Performance

**Faster Trash** ([4afa86a](https://github.com/inventory69/simple-notes-sync/commit/4afa86a))
- Trash deletion is batched and opening the trash is noticeably quicker

### 🐛 Bug Fixes

**EXIF Rotation & Aspect Ratio** ([41a9149](https://github.com/inventory69/simple-notes-sync/commit/41a9149), [d126a6f](https://github.com/inventory69/simple-notes-sync/commit/d126a6f), [9aa8927](https://github.com/inventory69/simple-notes-sync/commit/9aa8927))
- PDF export and preview honor EXIF orientation, keep the correct aspect ratio for rotated images, and reload images when a resize changes

**Editor Polish** ([1a5b0ca](https://github.com/inventory69/simple-notes-sync/commit/1a5b0ca), [30c2ebd](https://github.com/inventory69/simple-notes-sync/commit/30c2ebd), [4a0c561](https://github.com/inventory69/simple-notes-sync/commit/4a0c561))
- The cursor lands after inserted link and image Markdown, the image info button shows even without EXIF data, and collapsed sections are remembered per folder

**Spanish Accents** ([eeb847e](https://github.com/inventory69/simple-notes-sync/commit/eeb847e))
- Corrected accents in the Spanish sync and backup strings

**Language Picker** ([d3fc8dd](https://github.com/inventory69/simple-notes-sync/commit/d3fc8dd))
- French was fully translated but never registered, so it never appeared as a choice; the in-app picker used on Android 12 and older was also missing Spanish, Italian, Indonesian, Norwegian and Russian

### 🌍 Translations

- **French** (93%): Fred, [@carotte31](https://github.com/carotte31)
- **Italian** (93%): GVE
- **Chinese (Simplified)** (93%): [@heretic43](https://github.com/heretic43)
- **Estonian** (2%): Priit Jõerüüt

**French is now selectable** — it was translated but never registered in the language picker.

**Hindi (5%) and Estonian (2%) are not shipped yet.** A language is only bundled and offered in the picker once it reaches 40% coverage; below that the UI would be almost entirely English anyway. Both stay in Weblate and ship as soon as they pass the bar - contributions welcome.

---

## [2.11.0] - 2026-07-06

### ✨ New Features

**Note Archiving** ([4a306b5](https://github.com/inventory69/simple-notes-sync/commit/4a306b5))
- Archive notes to move them out of the main list without deleting them, with a dedicated archive view to restore or manage them later

**Default Note Color & Cursor Focus** ([cfdf542](https://github.com/inventory69/simple-notes-sync/commit/cfdf542))
- Choose a default color applied to every new note, and optionally place the cursor in the body right away when creating a note

**Per-Folder Sort Persistence** ([4437419](https://github.com/inventory69/simple-notes-sync/commit/4437419))
- Each folder remembers its own sort option and direction across restarts

**Configurable Preview Length** ([43f6eb0](https://github.com/inventory69/simple-notes-sync/commit/43f6eb0))
- Pick how many lines of preview text notes show in the list

**Markdown Toolbar Code Block** ([99af39d](https://github.com/inventory69/simple-notes-sync/commit/99af39d))
- New toolbar button inserts a fenced code block while editing

**Unified Sync-Target Change Flow** ([c0ede47](https://github.com/inventory69/simple-notes-sync/commit/c0ede47), [47844b0](https://github.com/inventory69/simple-notes-sync/commit/47844b0), [13be3fa](https://github.com/inventory69/simple-notes-sync/commit/13be3fa), [3ecf658](https://github.com/inventory69/simple-notes-sync/commit/3ecf658))
- Server and sync-folder changes now go through a single gate that confirms data handling before switching; the server subtitle shows the active folder, and the Markdown export folder is surfaced as a card with a copy action

### 🐛 Bug Fixes

**Markdown Formatting in PDF Export** ([495646f](https://github.com/inventory69/simple-notes-sync/commit/495646f), [41ceb09](https://github.com/inventory69/simple-notes-sync/commit/41ceb09), [0fb65dc](https://github.com/inventory69/simple-notes-sync/commit/0fb65dc))
- PDF export now renders real Markdown formatting instead of raw text, fills blank code lines, adds an inline-code background, and stops `snake_case` identifiers from rendering as italic
- Thanks to [@hoseinhzk](https://github.com/hoseinhzk) for the report!

**Password Reveal Re-arm** ([f32ee55](https://github.com/inventory69/simple-notes-sync/commit/f32ee55))
- Revealing the saved password again now requires a full clear first, so it can't be peeked after a partial edit

**Sort Flash & First-Time Gate** ([c58001a](https://github.com/inventory69/simple-notes-sync/commit/c58001a), [3ebe7d2](https://github.com/inventory69/simple-notes-sync/commit/3ebe7d2))
- Each folder pane sorts by its own setting to stop a visual flash, and the remote-target gate is suppressed during first-time setup

**Sync Folder Handling** ([d5f606a](https://github.com/inventory69/simple-notes-sync/commit/d5f606a), [ed472a1](https://github.com/inventory69/simple-notes-sync/commit/ed472a1), [0c6e0e9](https://github.com/inventory69/simple-notes-sync/commit/0c6e0e9))
- Connection test detects an existing Markdown folder, the folder-change dialog stays open until the restore finishes, and folder metadata is reset before a REPLACE restore

**List & Editor Polish** ([9dbfd98](https://github.com/inventory69/simple-notes-sync/commit/9dbfd98), [d0a4f9e](https://github.com/inventory69/simple-notes-sync/commit/d0a4f9e), [24a2a5b](https://github.com/inventory69/simple-notes-sync/commit/24a2a5b))
- The empty state scrolls so pull-to-refresh works, untitled notes fill the title slot with a content preview, and calendar export keeps the full checklist item text in the title

### 🌍 Translations

- **Italian** (48%): GVE
- **Chinese (Simplified)** (99%): [@heretic43](https://github.com/heretic43)

---

## [2.10.0] - 2026-07-01

### ✨ New Features

**Biometric App Lock** ([a6e1f20](https://github.com/inventory69/simple-notes-sync/commit/a6e1f20), [a2f0628](https://github.com/inventory69/simple-notes-sync/commit/a2f0628))
- Opt-in app lock backed by fingerprint, face, or device PIN, with a configurable grace period before re-locking
- `FLAG_SECURE` blocks screenshots/screen recording and hides content in the Recents thumbnail while locked

**Configurable Trash Retention** ([e6b2df3](https://github.com/inventory69/simple-notes-sync/commit/e6b2df3))
- Choose how long deleted notes stay in Trash before being purged: Immediately, 7, 14, 30 (default), or 90 days
- Thanks to [@xdpirate](https://github.com/xdpirate) for the request!

**Rich Clipboard Paste** ([bf003d1](https://github.com/inventory69/simple-notes-sync/commit/bf003d1))
- Pasting HTML from Telegram, Word, Google Docs, or a browser now converts formatting (bold/italic/links/lists/headings/code/blockquotes) to Markdown instead of dropping it, with a plain-text fallback

**Collapsible & Reorderable Sections** ([6d50b81](https://github.com/inventory69/simple-notes-sync/commit/6d50b81))
- Pinned/Folders/Notes section headers can be collapsed, and long-pressed to reorder; both persist across restarts

### 🐛 Bug Fixes

**Scroll Freeze on Long Previews** ([a51394c](https://github.com/inventory69/simple-notes-sync/commit/a51394c))
- Note preview text is now truncated before layout, preventing an ANR while scrolling lists with very long notes

**Security Settings Subtitle** ([ddbc3a4](https://github.com/inventory69/simple-notes-sync/commit/ddbc3a4))
- The Security subtitle now says "Biometric unlock off" instead of just "Off"

### ⚡ Performance Improvements

**Faster Note (De)serialization** ([452e557](https://github.com/inventory69/simple-notes-sync/commit/452e557), [e084759](https://github.com/inventory69/simple-notes-sync/commit/e084759))
- `Note.fromJson()` now streams JSON instead of building a full parse tree and using reflection, reducing cold-start time with thousands of notes

**Faster List Rendering & Sync Bookkeeping** ([845c3ec](https://github.com/inventory69/simple-notes-sync/commit/845c3ec), [4d7a240](https://github.com/inventory69/simple-notes-sync/commit/4d7a240), [50bfebe](https://github.com/inventory69/simple-notes-sync/commit/50bfebe), [1cba7a7](https://github.com/inventory69/simple-notes-sync/commit/1cba7a7))
- Pinned/unpinned split and checklist preview sort are cached instead of recomputed every frame
- Note sort/filter/search now run off the main thread
- `DeletionTracker` is backed by a map instead of a list
- `NotesStorage.loadAllNotes` reads files in parallel

### 🌍 Translations

- **Spanish** (66%): [@PaikLC](https://github.com/PaikLC) / Francisco Isaac Ordoñez Pedrero, [@Kiimby](https://github.com/Kiimby) / Kimby
- **Indonesian** (100%): [@arifpedia](https://github.com/arifpedia) / Arif Budiman
- **Italian** (46%): GVE
- **Norwegian Bokmål** (100%): [@xdpirate](https://github.com/xdpirate)
- **Chinese (Simplified)** (100%): [@heretic43](https://github.com/heretic43)

---

## [2.9.0] - 2026-06-22

### ✨ New Features

**In-App Changelog** ([60a275c](https://github.com/inventory69/simple-notes-sync/commit/60a275c), [d4e2702](https://github.com/inventory69/simple-notes-sync/commit/d4e2702), [db7a682](https://github.com/inventory69/simple-notes-sync/commit/db7a682))
- A native Changelog screen is now available directly in the app, reachable from Settings and via a "View changelog" button in the "What's New" sheet
- Changelog and Contributors are grouped under an "App" section in About

**Contributors Screen** ([5c614ca](https://github.com/inventory69/simple-notes-sync/commit/5c614ca))
- A new Contributors screen in About credits everyone who has contributed to the project

**Selectable Markdown Preview** ([b07a112](https://github.com/inventory69/simple-notes-sync/commit/b07a112))
- Text in the markdown preview can now be selected and copied
- Thanks to [@victoriamontero443-wq](https://github.com/victoriamontero443-wq) for the request!

### 🐛 Bug Fixes

**Developer Options Visibility** ([fb9a97f](https://github.com/inventory69/simple-notes-sync/commit/fb9a97f))
- Once unlocked, developer options stay visible until the app process is restarted instead of disappearing

### 🌍 Translations

- **Spanish** (64%): [@PaikLC](https://github.com/PaikLC) / Francisco Isaac Ordoñez Pedrero
- **Chinese (Simplified)** (99%): [@heretic43](https://github.com/heretic43)

---

## [2.8.0] - 2026-06-16

### ✨ New Features

**Trash / Recycle Bin** ([1d6fa60](https://github.com/inventory69/simple-notes-sync/commit/1d6fa60), [d2dd414](https://github.com/inventory69/simple-notes-sync/commit/d2dd414))
- Deleting a note now moves it to a Trash instead of erasing it; a new Trash screen (reachable from Settings) lets you restore or permanently delete notes
- Trashed notes are automatically purged after a retention period
- Notes deleted on the server are routed to the local Trash instead of vanishing, so a remote delete can still be recovered ([7d9d4b3](https://github.com/inventory69/simple-notes-sync/commit/7d9d4b3))

**Local-Only Folders** ([6a0dd97](https://github.com/inventory69/simple-notes-sync/commit/6a0dd97), [72cb314](https://github.com/inventory69/simple-notes-sync/commit/72cb314))
- Folders can now be marked local-only so their notes never sync to your WebDAV server - ideal for private or device-specific notes
- Thanks to [@alas-he](https://github.com/alas-he) for the request (discussion #88)!

**Scrollable Notes-List Widget** ([ca30ad7](https://github.com/inventory69/simple-notes-sync/commit/ca30ad7), [0566ce9](https://github.com/inventory69/simple-notes-sync/commit/0566ce9))
- A new home-screen widget shows a scrollable list of your notes with inline markdown formatting ([11c069f](https://github.com/inventory69/simple-notes-sync/commit/11c069f))
- Options to hide the header, hide pinned, hide folders, filter by folder, and set the font size
- Thanks to [@lincomax](https://github.com/lincomax) for the requests!

**Live Markdown in the Editor & Previews** ([9c96cc0](https://github.com/inventory69/simple-notes-sync/commit/9c96cc0), [17ad8d8](https://github.com/inventory69/simple-notes-sync/commit/17ad8d8))
- The editor now highlights markdown live and lets you tap checkboxes to toggle tasks
- Note list and grid card previews render markdown, including checklist ☑/☐ prefixes and styled fenced code blocks ([f48209a](https://github.com/inventory69/simple-notes-sync/commit/f48209a), [a4324a6](https://github.com/inventory69/simple-notes-sync/commit/a4324a6))

**Font Size Options** ([30de416](https://github.com/inventory69/simple-notes-sync/commit/30de416), [2cda14d](https://github.com/inventory69/simple-notes-sync/commit/2cda14d))
- New app-wide font size setting plus an independent per-widget font size
- Thanks to [@lincomax](https://github.com/lincomax) for the request!

**Add Checklist Items to Calendar** ([82f3bfc](https://github.com/inventory69/simple-notes-sync/commit/82f3bfc))
- Each checklist item now has an "Add to calendar" entry in its context menu
- Thanks to Papa for the request!

**Default Open Mode Toggle** ([5a70f14](https://github.com/inventory69/simple-notes-sync/commit/5a70f14), [1882210](https://github.com/inventory69/simple-notes-sync/commit/1882210))
- Choose whether text notes open in edit or preview mode by default (now edit by default)

### 🐛 Bug Fixes

**Sync Loop & Folder Fixes** ([516cd15](https://github.com/inventory69/simple-notes-sync/commit/516cd15), [741ba37](https://github.com/inventory69/simple-notes-sync/commit/741ba37), [f3062c0](https://github.com/inventory69/simple-notes-sync/commit/f3062c0))
- Tied-timestamp edits now adopt the server copy to break an upload/download loop
- Notes moved during a folder rename are no longer deleted from the server
- Moving a note into a local-only folder is correctly ledgered as a server deletion

**Backup Includes Folder Metadata** ([a854fb9](https://github.com/inventory69/simple-notes-sync/commit/a854fb9))
- Backups now store and restore folder metadata instead of losing it on restore

**Notification Permission Prompt Only Once** ([58f6e7c](https://github.com/inventory69/simple-notes-sync/commit/58f6e7c))
- The notification permission is now auto-requested only once instead of on repeated launches
- Thanks to [@xdpirate](https://github.com/xdpirate) for the report!

**Editor & Markdown Polish** ([552f44d](https://github.com/inventory69/simple-notes-sync/commit/552f44d), [27ae90d](https://github.com/inventory69/simple-notes-sync/commit/27ae90d), [e15ecb0](https://github.com/inventory69/simple-notes-sync/commit/e15ecb0))
- Markdown toolbar shows only when the content field is focused, and is hidden while editing the title
- Fixed adjacent inline delimiters and italic detection not rendering correctly

**Widget Fixes** ([ec761f7](https://github.com/inventory69/simple-notes-sync/commit/ec761f7), [a9aa973](https://github.com/inventory69/simple-notes-sync/commit/a9aa973), [cb6a937](https://github.com/inventory69/simple-notes-sync/commit/cb6a937))
- Correct header/pinned visibility, full-height resize, and automatic day/night text color

**Checklist Animation Fixes** ([a6002fd](https://github.com/inventory69/simple-notes-sync/commit/a6002fd), [cac2e3b](https://github.com/inventory69/simple-notes-sync/commit/cac2e3b), [de5347a](https://github.com/inventory69/simple-notes-sync/commit/de5347a))
- Empty items are preserved when backgrounding the editor, the check animation plays on undo/redo, and items sort immediately after note-to-checklist conversion

**Other UI Fixes** ([5f3c10e](https://github.com/inventory69/simple-notes-sync/commit/5f3c10e), [c3bf07f](https://github.com/inventory69/simple-notes-sync/commit/c3bf07f), [40c9148](https://github.com/inventory69/simple-notes-sync/commit/40c9148))
- The help button and pull-to-refresh now react immediately to server configuration changes
- The create-folder dialog no longer expands too wide, and the multiselect top bar shows only the count

### 🔧 Technical

**Dependency Updates** ([4d5cc1e](https://github.com/inventory69/simple-notes-sync/commit/4d5cc1e))
- Compose BOM 2026.03.00 - 2026.05.01 (Foundation 1.11, Material3 1.4)
- Material 1.13.0 - 1.14.0
- Coroutines 1.10.2 - 1.11.0
- Gson 2.13.2 - 2.14.0
- WorkManager 2.11.1 - 2.11.2
- MockK 1.14.9 - 1.14.11
- Compose Foundation 1.11 suppresses `animateItem` placement animations during `animateScrollToItem()`; disabled the upstream rollout flag to restore 1.10 behaviour until the upstream fix lands ([b/493183465](https://issuetracker.google.com/issues/493183465))

---

## [2.7.2] - 2026-06-07

### 🐛 Bug Fixes

**Sync: Folder & Deleted-Flag Heal on no-ETag WebDAV Servers** ([1d3a5a1](https://github.com/inventory69/simple-notes-sync/commit/1d3a5a1))
- On WebDAV servers that never return ETags, skipped notes could keep a stale `folderName` or a false `DELETED_ON_SERVER` flag indefinitely; both cases are now healed on each skip without triggering a re-upload loop

**UI: Notes List Stays at Top on Cold Start** ([a9d4533](https://github.com/inventory69/simple-notes-sync/commit/a9d4533))
- The staggered grid auto-scrolled away from index 0 on initial load (Compose Foundation quirk), hiding the "Pinned" section header; a top-settle guard now holds the list at the top until the layout stabilises

**Widget: OptionsBar Icons Visible in Light Mode** ([c448a43](https://github.com/inventory69/simple-notes-sync/commit/c448a43))
- Widget OptionsBar icons (lock, refresh, settings, open-in-app) had a hardcoded white fill and were invisible on light backgrounds; they now adapt to the system theme via `ColorFilter.tint(onSurface)`

### 🌍 Translations

- **Spanish**: continued expansion — Francisco Isaac Ordoñez Pedrero
- **Indonesian**: updated — Arif Budiman
- **Simplified Chinese**: updated — heretic43
- **Norwegian Bokmål**: updated — xdpirate

---

## [2.7.1] - 2026-06-02

### 🐛 Bug Fixes

**Folder Sync: Server Orphan Files** ([77b4029](https://github.com/inventory69/simple-notes-sync/commit/77b4029))
- Notes moved back to root during folder deletion are now queued for server deletion from their old subfolder path, preventing orphan files on the server
- Fixed URL-encoded folder names (spaces as `%20`) causing `deleteServerFolderIfEmpty` to skip directory cleanup
- Empty server folder directories are now cleaned up after batch pending deletions complete ([4e05ad1](https://github.com/inventory69/simple-notes-sync/commit/4e05ad1))

**Folder Sync: Notes Stuck in PENDING After Folder Move** ([18f186a](https://github.com/inventory69/simple-notes-sync/commit/18f186a))
- Server scan now deduplicates notes by `noteId`; after a folder move the same note could appear in multiple WebDAV directories, causing conflicting folder assignments and an endless PENDING state

**Sync: Silent Sync No Longer Clears Visible Banners** ([1f43b50](https://github.com/inventory69/simple-notes-sync/commit/1f43b50))
- Background silent syncs can no longer clear or replace a result banner whose auto-hide timer is still running

**UI: Banner Content Flash During Dismiss** ([af07399](https://github.com/inventory69/simple-notes-sync/commit/af07399))
- Fixed a blank flash in the sync result banner when `AnimatedContent` received `IDLE` during the dismiss animation; visible content now remains stable until the animation completes

**UI: Create Folder Dialog Polish** ([fb72623](https://github.com/inventory69/simple-notes-sync/commit/fb72623))
- Keyboard now opens automatically when the dialog appears (no extra tap required)
- First letter of the folder name is auto-capitalized

**UI: Folder Deletion Banner Plural String** ([99dfbae](https://github.com/inventory69/simple-notes-sync/commit/99dfbae))
- Fixed the confirmation banner after folder deletion: now uses the correct plural resource depending on whether notes were deleted locally or also erased on the server

### 🌍 Translations

- **Spanish**: expanded from partial to broad coverage, terminology fix (`borrar` → `eliminar`) ([2f19d18](https://github.com/inventory69/simple-notes-sync/commit/2f19d18)), locale registered in `locales_config` ([974efe1](https://github.com/inventory69/simple-notes-sync/commit/974efe1))
- **Simplified Chinese**: 7 debug screen strings added (100% coverage) ([2f19d18](https://github.com/inventory69/simple-notes-sync/commit/2f19d18))

---

## [2.7.0] - 2026-05-30

### ✨ New Features

**Folder Support** ([e38553d](https://github.com/inventory69/simple-notes-sync/commit/e38553d))
- Notes can be organized into folders; each note carries an optional `folderName` field
- Create, rename, and delete folders via dedicated CRUD dialogs
- Move notes to a folder from a bottom sheet accessible in the note list
- Folder navigation in the main screen filters the list by folder
- Folders are persisted with tombstone-based deletion tracking (`FolderStore`)
- Folders sync bidirectionally via WebDAV using folder-aware URL routing (`FolderSyncManager`)
- Thanks to [@happy-turtle](https://github.com/happy-turtle) and [@racehd](https://github.com/racehd) for the idea, and [@afoni95](https://github.com/afoni95) for testing!

### 🐛 Bug Fixes

**Grid & Pinned Notes Polish** ([1e02895](https://github.com/inventory69/simple-notes-sync/commit/1e02895))
- Fixed pop-in artifact when pinned notes appeared in staggered grid
- Grid scroll now resets to top when the active filter changes ([2375c97](https://github.com/inventory69/simple-notes-sync/commit/2375c97))
- Staggered grid split calculations memoized, reducing layout churn ([dc117ba](https://github.com/inventory69/simple-notes-sync/commit/dc117ba))
- Fixed visual glitch in GridColumnChip in the display settings column picker ([27a7c0a](https://github.com/inventory69/simple-notes-sync/commit/27a7c0a))

### 🌍 Translations

- **Spanish** (new): [08f0948](https://github.com/inventory69/simple-notes-sync/commit/08f0948)

---

## [2.6.0] - 2026-05-24

### ✨ New Features

**Pinned Notes Section** ([e849bdf](https://github.com/inventory69/simple-notes-sync/commit/e849bdf))
- Notes can be pinned via multi-select batch action; pinned notes appear in a dedicated "Pinned" section above the regular list
- Section only visible when at least one note is pinned; pinned cards show a pin icon indicator
- Works in both list and grid view modes
- Requested by [@ASFHU](https://github.com/ASFHU), [@james0336](https://github.com/james0336), [@isawaway](https://github.com/isawaway)!

**Type Conversion: Text ↔ Checklist** ([714a7e6](https://github.com/inventory69/simple-notes-sync/commit/714a7e6))
- Convert any text note to a checklist and back; GFM checkbox notation (`- [ ]` / `- [x]`) is preserved in both directions
- Full undo/redo support for type conversions

**Auto-Link Bare URLs in Markdown Preview** ([745a4cd](https://github.com/inventory69/simple-notes-sync/commit/745a4cd))
- Naked `http://` and `https://` URLs are now detected and rendered as clickable links in the Markdown preview
- Trailing sentence punctuation is excluded from the linked URL
- Requested by [@james0336](https://github.com/james0336), [@isawaway](https://github.com/isawaway)!

**"Copy Text" in Editor Overflow Menu** ([83e6a3c](https://github.com/inventory69/simple-notes-sync/commit/83e6a3c))
- One-tap copy of the full note content (title + body) to the clipboard; Android 13+ shows the system confirmation, older versions show a Snackbar
- Thanks to [@xdpirate](https://github.com/xdpirate) for the suggestion!

**Append Shared Text to Existing Note** ([dc59351](https://github.com/inventory69/simple-notes-sync/commit/dc59351))
- Share intent now offers a third option to append shared text to an existing note instead of always creating a new one
- A note picker (sorted by last modified) lets you choose the target; the editor opens with the merged content for review

### 🐛 Bug Fixes

**Settings Not Reactive to Offline Mode Toggle** ([b388016](https://github.com/inventory69/simple-notes-sync/commit/b388016))
- `isServerConfigured` was a plain function called once per composable, so toggling offline mode did not trigger recomposition; replaced with a `StateFlow` derived via `combine`/`map`
- Status labels now correctly distinguish "offline mode active" from "no server configured"

**Parallel Connections Setting Active in Offline Mode** ([c6cf4e5](https://github.com/inventory69/simple-notes-sync/commit/c6cf4e5))
- The parallel connections radio group remained interactive while all other sync settings were grayed out in offline mode; it is now correctly disabled

**Checklist: Unicode Bullet/Checkmark Prefixes Not Stripped on Share Paste** ([8e67ccd](https://github.com/inventory69/simple-notes-sync/commit/8e67ccd))
- `• item` (U+2022), `✓ item`, `☑ item`, and `✔ item` are now normalised correctly when pasting via share intent

**Checklist: Separator Jumps on Keyboard Show/Hide** ([8295522](https://github.com/inventory69/simple-notes-sync/commit/8295522))
- The checked/unchecked separator was missing `animateItem()`, causing it to snap instead of slide when the soft keyboard resized the list

**Checklist: Mixed GFM + Plain Lines Not Parsed Per-Line** ([2bd8106](https://github.com/inventory69/simple-notes-sync/commit/2bd8106))
- Mixed input (some GFM, some plain lines) fell through to the raw path, leaving `- [ ] Milk` as literal item text; the parser now classifies each line individually

---

## [2.5.2] - 2026-05-22

### 🐛 Bug Fixes

**Navigation Bar Color Not Applied on Android < 10 in Dark Mode** ([5554710](https://github.com/inventory69/simple-notes-sync/commit/5554710))
- On API < 30, the navigation bar color was not applied in dark mode; the window flag `navigationBarColor` is now set explicitly for these devices
- Thanks to [dfxflkbvnsr](https://github.com/dfxflkbvnsr) for the report!

**Snackbar Overlapping the FAB on Main Screen** ([a9ff4f5](https://github.com/inventory69/simple-notes-sync/commit/a9ff4f5))
- The snackbar appeared behind the floating action button; it now renders above the FAB
- Thanks to [dfxflkbvnsr](https://github.com/dfxflkbvnsr) for the report!

**Back Press Ignored in Note Selection Mode** ([b8689eb](https://github.com/inventory69/simple-notes-sync/commit/b8689eb))
- Pressing back while notes were selected did not exit selection mode; the back handler is now correctly registered when selection is active
- Thanks to [dfxflkbvnsr](https://github.com/dfxflkbvnsr) for the report!

**Multi-Select Color Picker Showed No Current Color** ([1365893](https://github.com/inventory69/simple-notes-sync/commit/1365893))
- The color picker in multi-select always opened with no color pre-selected; it now highlights the shared color when all selected notes share one

**Note Color Lost When Entering Selection Mode** ([9c4cc5d](https://github.com/inventory69/simple-notes-sync/commit/9c4cc5d))
- Tapping a note card to begin selection reset its color to None; the color is now preserved

**Checklist UNCHECKED_FIRST Sort Stability on Toggle** ([2c008fd](https://github.com/inventory69/simple-notes-sync/commit/2c008fd))
- `UNCHECKED_FIRST` shared the same `sortedBy { originalOrder }` branch as `MANUAL`, causing items to jump on every toggle: unchecked items moved to the top of the unchecked group, checked items sank to the bottom of the checked group
- Fixed with a dedicated stable-filter branch that preserves relative order within each group; `MANUAL` keeps its `originalOrder`-based restore unchanged
- Thanks to [@WuTangWilli](https://github.com/WuTangWilli) for the report!

### 🌍 Translations

- **Indonesian** (100%): [@arifpedia](https://github.com/arifpedia) / Arif Budiman
- **Chinese (Simplified)** (100%): heretic43
- **Hindi** (partial): Silent Coder

---

## [2.5.1] - 2026-05-16

> 🎉 **First Google Play production release** — v2.5.0 was prepared as the initial production build but was never published to the Play Store. v2.5.1 is the version actually reaching production. It completes the note colour feature that shipped in v2.5.0 by adding the one missing piece: sorting by colour.

### ✨ New Features

**Note List Colour Sorting** ([93f7852](https://github.com/inventory69/simple-notes-sync/commit/93f7852))
- New **Color** option in the Sort dialog groups notes by colour in canonical palette order (Red → Orange → Yellow → Green → Teal → Blue → Dark blue → Purple → Pink → Brown → Gray)
- Uncoloured notes land at the end (ascending) or the beginning (descending) of the list
- Within each colour group, notes are ordered by last-modified date descending — consistent with the existing Type sort
- Sort selection persists across app restarts via SharedPreferences

---

## [2.5.0] - 2026-05-15

> 🎉 **First Google Play production release!** After several rounds of beta testing, v2.5.0 is the first build promoted to the Play Store production track. A huge thank you to everyone who installed early builds, reported issues and helped us shake out the rough edges.

### ✨ New Features

**Google Keep Import** ([#65](https://github.com/inventory69/simple-notes-sync/issues/65))
- Import notes directly from a Google Keep / Google Takeout `.zip` archive — fully on-device, no upload
- Pre-scan classifies the archive (active / archived / trashed / labels / shared / total size) before you confirm
- Per-run **conflict strategy**: *always create* (default), *skip if exists*, or *replace if exists* — matched by content hash (title + body + checklist items)
- Optional **archived** and **trashed** include switches
- Indented checklists preserved up to 3 levels (DnD behaviour unchanged)
- Keep `color` is imported and applied immediately (see *Note Colours* below); `isPinned` is persisted in the note model (rendering follows in a later release)
- Labels persisted in a separate `notes_labels.json` index
- Cancellable mid-import; already-imported notes are kept; result dialog shows imported / replaced / skipped / errors with an expandable error list
- Confirmation step for archives larger than 200 MB
- Single auto-sync triggered at the end (only when ≥ 1 note was imported or replaced; respects existing on-save sync gate, throttle, and offline-mode settings)
- Find it under **Settings → Import → Import from Google Keep**
- Thanks to [@simianaya](https://github.com/simianaya) for the feature request!

**Note Colours** ([#65](https://github.com/inventory69/simple-notes-sync/issues/65))
- New colour palette (None, Red, Orange, Yellow, Green, Teal, Blue, Dark blue, Purple, Pink, Brown, Gray) with separate light/dark slots so colours stay readable in both themes ([4465615](https://github.com/inventory69/simple-notes-sync/commit/4465615))
- Note cards on the home screen are tinted by the chosen colour ([7ced207](https://github.com/inventory69/simple-notes-sync/commit/7ced207))
- New colour picker bottom sheet ([cd0361e](https://github.com/inventory69/simple-notes-sync/commit/cd0361e))
- Set a note's colour from the editor's overflow menu ([b244db9](https://github.com/inventory69/simple-notes-sync/commit/b244db9))
- Bulk-set the colour for multiple selected notes from the home screen ([7341462](https://github.com/inventory69/simple-notes-sync/commit/7341462))
- In the editor, the colour is rendered as a subtle 3 dp accent stripe instead of a full-screen tint, so reading stays comfortable ([bd5c943](https://github.com/inventory69/simple-notes-sync/commit/bd5c943))
- Colours from Google Keep imports are mapped to this palette automatically
- Thanks to [@simianaya](https://github.com/simianaya) for the feature request!

**Conflict Strategy for Local & WebDAV Imports**
- The conflict strategy that already shipped with the Keep import is now also available for the **local file import** ([3a3d55c](https://github.com/inventory69/simple-notes-sync/commit/3a3d55c)) and the **WebDAV scan import** ([d369536](https://github.com/inventory69/simple-notes-sync/commit/d369536))
- Choose per import run whether to *always create*, *skip*, or *replace* matching notes — same content-hash comparison as the Keep import, so behaviour is consistent across all three sources
- Local file import button now uses the standard `SettingsButton` for visual consistency ([75f4dbd](https://github.com/inventory69/simple-notes-sync/commit/75f4dbd))

**Select All / Deselect All for WebDAV Import** ([d928ddb](https://github.com/inventory69/simple-notes-sync/commit/d928ddb))
- After scanning a WebDAV folder, a single toggle lets you tick or untick every result at once
- Saves a lot of tapping when restoring large note sets

**Note List Colour Filter** ([e773a05](https://github.com/inventory69/simple-notes-sync/commit/e773a05))
- Palette icon chip in the filter bar opens a colour-filter dropdown with per-colour note counts
- Combines with the existing type filter (AND logic) and is persisted in SharedPreferences
- Text and List type chips converted to icon-only for visual consistency
- Closes [#65](https://github.com/inventory69/simple-notes-sync/issues/65)

### ✨ Improvements

**Checklist Tap Animation**
- Ticking a checklist item now plays a brand-new animation: a brief scale-pop on the row, a soft radial glow and a satisfying check-off effect ([7156c12](https://github.com/inventory69/simple-notes-sync/commit/7156c12), [2551d21](https://github.com/inventory69/simple-notes-sync/commit/2551d21))
- The first item no longer "jumps" when the editor opens — the placement animation is suppressed on initial render ([2515874](https://github.com/inventory69/simple-notes-sync/commit/2515874))
- The list keeps its scroll position correctly when items move between the unchecked and checked branches ([9792968](https://github.com/inventory69/simple-notes-sync/commit/9792968))

### 🐛 Bug Fixes

- **Imports always assign `SyncStatus.PENDING`** ([51ea959](https://github.com/inventory69/simple-notes-sync/commit/51ea959)) — imported notes are now reliably picked up by the next sync run, regardless of the import source
- **Editor autosave includes colour changes** ([a0a34b9](https://github.com/inventory69/simple-notes-sync/commit/a0a34b9)) — switching a note's colour from the editor no longer gets discarded by the no-change guard

### ♻️ Internal

**`SyncScheduler` extracted**
- The duplicated `triggerOnSaveSync()` logic from `NoteEditorViewModel` and `MainViewModel` is now centralised in `sync/SyncScheduler` (behaviour strictly equivalent to v2.4.0; reused by the Keep import flow)

**APK Size −11% via Locale & Resource Filtering** ([5a631cb](https://github.com/inventory69/simple-notes-sync/commit/5a631cb))
- `localeFilters` restricted to the 10 supported languages — removes unused translations from `resources.arsc`
- Strict resource shrinking (`keep.xml`) — safe because the app has no dynamic resource lookups
- Packaging excludes expanded to remove build-time-only metadata
- Result: 5.23 MB → 4.65 MB (−577 KB, −11%); `resources.arsc`: 1.49 MB → 0.95 MB (−540 KB)

### 📚 Documentation

- New user guide: `project-docs/simple-notes-sync/v2.5.0/google-keep-import-user-guide.md` (EN + DE)
- New QA cheatsheet: `project-docs/simple-notes-sync/v2.5.0/google-keep-import-adb-tests.md`

### 🙏 Acknowledgements

- [@simianaya](https://github.com/simianaya) — for requesting Google Keep import and note colours ([#65](https://github.com/inventory69/simple-notes-sync/issues/65))
- Everyone who tested early Google Keep exports and reported edge cases
- All beta testers who helped us reach the **first Play Store production release** — thank you! 🎉

---

## [2.4.0] - 2026-05-04

### ✨ New Features

**Persistent Sync Debug Logger** ([1de5fdb](https://github.com/inventory69/simple-notes-sync/commit/1de5fdb), [1234f6c](https://github.com/inventory69/simple-notes-sync/commit/1234f6c), [d8d4284](https://github.com/inventory69/simple-notes-sync/commit/d8d4284), [13defa9](https://github.com/inventory69/simple-notes-sync/commit/13defa9), [62e75e7](https://github.com/inventory69/simple-notes-sync/commit/62e75e7))
- New persistent `SyncDebugLogger` writing structured entries to `sync_debug.log` for diagnosing background-sync issues
- Logs every `SyncWorker` outcome (start, success, soft errors, retries, final failure)
- Schema enriched with `attempt` counter and `holder` (process tag) to distinguish app/worker contexts
- WIFI_CONNECT events annotated with `first|change` reason for clarity
- Explicit final outcome emitted when WorkManager drops retries, so failures aren't invisible
- **Disabled by default** ([26cdf47](https://github.com/inventory69/simple-notes-sync/commit/26cdf47)) — enable in Settings → Debug to collect logs when reporting sync issues

**Bypass Cold-Start Guard After Long Process Gap** ([17d57d5](https://github.com/inventory69/simple-notes-sync/commit/17d57d5))
- After a long period without app process activity (e.g. overnight), the cold-start sync guard is bypassed so the first sync runs immediately on resume

### 🐛 Bug Fixes

**Reliable WiFi-Connect Sync Trigger**
- Require validated WiFi (not just SSID) before triggering sync ([a03eadf](https://github.com/inventory69/simple-notes-sync/commit/a03eadf))
- Retry WiFi-trigger sync on transient unreachability ([bea0558](https://github.com/inventory69/simple-notes-sync/commit/bea0558))
- Bypass global cooldown for WiFi-connect trigger so the first sync after reconnect actually runs ([5195ee0](https://github.com/inventory69/simple-notes-sync/commit/5195ee0))
- Cap WiFi-trigger backoff at 30 s linear to avoid runaway delays ([5ff2a70](https://github.com/inventory69/simple-notes-sync/commit/5ff2a70))
- Shorten WiFi-fallback interval to 30 min for faster process-death recovery ([42e6a64](https://github.com/inventory69/simple-notes-sync/commit/42e6a64))

**Cheaper, More Robust Reachability**
- Cheap reachability check before expensive PROPFIND ([f46f326](https://github.com/inventory69/simple-notes-sync/commit/f46f326))
- `monitoringStartTime` made volatile and switched to `elapsedRealtime()` to avoid wall-clock skew ([7f980fb](https://github.com/inventory69/simple-notes-sync/commit/7f980fb))
- Drop redundant onResume sync cooldown ([69a7b05](https://github.com/inventory69/simple-notes-sync/commit/69a7b05))

**Settings & Logging**
- Make debug-log export robust on all devices (handles SAF edge cases) ([bbf466b](https://github.com/inventory69/simple-notes-sync/commit/bbf466b))
- Clear `sync_debug.log` on the Delete-Logs button so users can reset the log easily ([caa329e](https://github.com/inventory69/simple-notes-sync/commit/caa329e))

### ♻️ Internal

**WebDAV Server Migration** ([41ecb13](https://github.com/inventory69/simple-notes-sync/commit/41ecb13))
- Self-hosted reference server migrated from `bytemark/webdav` to `hacdias/webdav` for active maintenance and better protocol coverage
- No user-visible change for app users; affects the optional bundled server in `server/`

### 🌍 Translations

- **New language: Indonesian (`in`)** — thanks to [Arif Budiman](https://hosted.weblate.org/user/aribudiman/) ([c537455](https://github.com/inventory69/simple-notes-sync/commit/c537455), [6bc7a4e](https://github.com/inventory69/simple-notes-sync/commit/6bc7a4e))
- **New language: Norwegian Bokmål (`nb`)** — thanks to [xdpirate](https://hosted.weblate.org/user/xdpirate/) ([3ce9164](https://github.com/inventory69/simple-notes-sync/commit/3ce9164), [d11b34d](https://github.com/inventory69/simple-notes-sync/commit/d11b34d))
- **New language: Italian (`it`)** — thanks to [Jean-Pierre](https://hosted.weblate.org/user/Jeannot/) ([3467756](https://github.com/inventory69/simple-notes-sync/commit/3467756), [4bf449c](https://github.com/inventory69/simple-notes-sync/commit/4bf449c))
- **New language: Hindi (`hi`)** — thanks to [Silent Coder](https://hosted.weblate.org/user/silentcoder/) ([2c54735](https://github.com/inventory69/simple-notes-sync/commit/2c54735), [9ba64fd](https://github.com/inventory69/simple-notes-sync/commit/9ba64fd), [ecfb129](https://github.com/inventory69/simple-notes-sync/commit/ecfb129))
- **New language: Russian (`ru`)** — thanks to [PONYATIN](https://hosted.weblate.org/user/PONYATIN/) ([4078a59](https://github.com/inventory69/simple-notes-sync/commit/4078a59), [f96055e](https://github.com/inventory69/simple-notes-sync/commit/f96055e), [1eeebc1](https://github.com/inventory69/simple-notes-sync/commit/1eeebc1))
- **Chinese (Simplified) updated** — thanks to [heretic43](https://hosted.weblate.org/user/heretic43/) ([7a2defb](https://github.com/inventory69/simple-notes-sync/commit/7a2defb))
- New locales `in` and `nb` registered in `locales_config.xml` ([349e972](https://github.com/inventory69/simple-notes-sync/commit/349e972)); `ru`, `it`, `hi` registered in [84a5282](https://github.com/inventory69/simple-notes-sync/commit/84a5282)

### 🙏 Acknowledgements

A huge thank you to the Weblate translators making Simple Notes Sync available in more languages:
- **Arif Budiman** — Indonesian
- **xdpirate** — Norwegian Bokmål
- **Jean-Pierre** — Italian
- **Silent Coder** — Hindi
- **PONYATIN** — Russian
- **heretic43** — Chinese (Simplified)

---

## [2.3.1] - 2026-04-24

> Beta-only release on Google Play. Not published on F-Droid; the changes are included in v2.4.0.

### 🐛 Bug Fixes

**Visibility-Aware Error Banner on Soft Sync Errors** ([c4a40f8](https://github.com/inventory69/simple-notes-sync/commit/c4a40f8))
- Silent background sync errors no longer surface as a red error banner — they reset cleanly to IDLE
- Errors are only promoted to a visible banner when the user is actively viewing the app
- New `SyncStateManager.errorIfVisible()` API used by `SyncWorker` and `MainViewModel.triggerAutoSync` for all soft-error paths
- Prevents user-confusing transient warnings during background retries

---

## [2.3.0] - 2026-04-18

### 🛡️ Security

**WebDAV Credentials Now Stored Encrypted** ([bf117f8](https://github.com/inventory69/simple-notes-sync/commit/bf117f8))
- WebDAV username and password were previously stored as plaintext in regular SharedPreferences
- Migrated to `EncryptedSharedPreferences` with AES256-GCM encryption
- One-time auto-migration on first start: existing credentials are moved to the encrypted store and removed from the plaintext store

### ✨ New Features

**Battery Optimization Prompt on Sync Enable & Migration** ([da2ab36](https://github.com/inventory69/simple-notes-sync/commit/da2ab36))
- When the user disables offline mode, the app immediately checks battery optimization exemption and shows the system dialog if needed
- One-time migration for existing users: users who already have sync enabled but were never prompted see the dialog once on next app start
- Uses new SharedPreferences key `battery_opt_migration_shown`

### 🐛 Bug Fixes

**Fix Markdown Auto-Sync Not Firing on Save** ([1756af4](https://github.com/inventory69/simple-notes-sync/commit/1756af4))
- SharedPreferences for markdown export/auto-import were only persisted after a successful initial export — if it failed (HTTP 405, timeout, network error), the prefs were never set and on-save export never fired
- Prefs are now persisted immediately after server config validation; initial export is best-effort
- HTTP 405 fallback added to `ensureMarkdownDirExists()` (list-after-failed-exists pattern)
- Thanks to [@minosimo](https://github.com/minosimo) for the detailed bug report and logs! ([#50](https://github.com/inventory69/simple-notes-sync/issues/50))

**Improve MKCOL 404 Handling and WebDAV Validation** ([8c5907a](https://github.com/inventory69/simple-notes-sync/commit/8c5907a))
- `SafeSardineWrapper.createDirectory()`: handle 404 with `list()` fallback (analogous to existing 405 handling)
- `WebDavSyncService.testConnection()`: verify WebDAV capability via PROPFIND after HEAD check to prevent false "Reachable" status
- `SyncExceptionMapper`: detect MKCOL failures and show user-friendly message with WebDAV URL hint
- Thanks to [@Ichigo-Meow](https://github.com/Ichigo-Meow) for reporting! ([#55](https://github.com/inventory69/simple-notes-sync/issues/55))

**Stop Phantom "Untitled" Notes from WebDAV Root Scan** ([fab23eb](https://github.com/inventory69/simple-notes-sync/commit/fab23eb))
- Foreign JSON files in the WebDAV root (e.g. `info.json`) were parsed on every sync, producing a fresh "Untitled" ghost note with a random UUID — saved as SYNCED, then immediately flagged DELETED_ON_SERVER, accumulating endlessly
- Disabled the legacy v1.2.0 root fallback in the normal sync path; the migration scan still runs in `restoreFromServer()`
- Added UUID-format and id-vs-filename guards in `NoteDownloader` Phase 2 as defense-in-depth
- Thanks to [@angeld-jr2](https://github.com/angeld-jr2) for the detailed debug log that made this diagnosable! ([#62](https://github.com/inventory69/simple-notes-sync/issues/62))

**Allow Title-less Notes in Backup Validation** ([d41d02b](https://github.com/inventory69/simple-notes-sync/commit/d41d02b))
- Backup restore rejected v2.2.0 backups containing notes with an empty title (e.g. checklists)
- `validateBackup()` now matches the editor: a note is only invalid when both title AND content/checklist-items are blank
- Thanks to [@angeld-jr2](https://github.com/angeld-jr2) for reporting!

**Map HTTP 401 During Directory Ensure to Auth Error** ([02c3f77](https://github.com/inventory69/simple-notes-sync/commit/02c3f77))
- `ensureNotesDirectoryExists()` and `ensureMarkdownDirectoryExists()` swallowed 401s and fell through to MKCOL, surfacing "Cannot create sync folder" instead of "Authentication failed"
- Auth errors are now detected and re-thrown before MKCOL; defense-in-depth in `SyncExceptionMapper`

**Align Widget Checklist Sort with Editor for All Sort Options** ([dcc740b](https://github.com/inventory69/simple-notes-sync/commit/dcc740b))
- Widget `ToggleChecklistItemAction` only handled MANUAL and UNCHECKED_FIRST
- Extracted shared `ChecklistSorter` utility used by both widget and editor for consistent sorting across all seven sort options
- Thanks to MrsMinchen for the contribution!

**Trigger Auto-Save on Sort Option Change** ([06c5228](https://github.com/inventory69/simple-notes-sync/commit/06c5228))
- `sortChecklistItems(option)` was missing `isDirty` and `scheduleAutosave`, so sort changes were lost without explicit save
- Widget updates also added after auto-save and saveOnBack to keep widgets in sync
- Thanks to freemen for reporting!

**Truncate Widget Content to Prevent TransactionTooLargeException** ([7aba796](https://github.com/inventory69/simple-notes-sync/commit/7aba796))
- Limit text notes to 100 lines and checklists to 100 items in widget rendering — very long notes exceeded the 1MB Binder IPC limit for RemoteViews

**Localize Widget Empty State and Add Tap-to-Reconfigure** ([0b7dbf8](https://github.com/inventory69/simple-notes-sync/commit/0b7dbf8))
- Replaced hardcoded "Note not found" with a string resource
- Tapping the widget now opens the config activity so users can recover after note data is cleared

**Move NotesStorage File I/O off the Main Thread** ([645ce9e](https://github.com/inventory69/simple-notes-sync/commit/645ce9e))
- `saveNote`, `loadNote`, `loadAllNotes`, `deleteNote` are now suspend functions on `Dispatchers.IO`
- Fixes `loadAllNotes()` race-condition crash (FileNotFoundException between listFiles/readText)
- Fixes empty TextFieldState on first note open after app start
- Fixes "Note marked PENDING on back-navigation from empty editor"
- Fixes "Widget showing 'Note not found'": `loadNoteSync()` now runs inside `provideContent` so every widget update reads fresh data

**Prevent Flash of Wrong Editor State on Async Note Load** ([b7b3a1c](https://github.com/inventory69/simple-notes-sync/commit/b7b3a1c))
- Async load surfaced TEXT-mode defaults for 1+ frames before the IO load completed, briefly showing wrong TopBar title and content type for checklists
- `isNewNote=false` is now set synchronously before launching the coroutine; isLoading gates the entire screen

**Use Mutex-Protected Deletion Tracking** ([e777259](https://github.com/inventory69/simple-notes-sync/commit/e777259))
- `deleteNote()` now uses `trackDeletionSafe()` to prevent race conditions during batch deletes; the legacy unprotected variant is deprecated

**Preserve List Scroll Position When Returning from Editor** ([c5b4955](https://github.com/inventory69/simple-notes-sync/commit/c5b4955))
- New-note detection moved from the unsorted load to the sorted-flow; editing an existing note no longer resets scroll

**Persist Navigation Flags Across Process Death** ([78b331b](https://github.com/inventory69/simple-notes-sync/commit/78b331b))
- `cameFromEditor`/`cameFromSettings` are saved/restored via `onSaveInstanceState`, preventing incorrect scroll-to-top and sync suppression after system process termination

**Trigger onSave Sync After Editor Deletion** ([5c7f008](https://github.com/inventory69/simple-notes-sync/commit/5c7f008))
- `deleteNoteFromEditor` now triggers a sync to propagate the deletion to the server immediately, consistent with `saveNote` behavior

**Add Reachability Check to Settings syncNow()** ([d1928be](https://github.com/inventory69/simple-notes-sync/commit/d1928be))
- The Settings "Sync now" path was the only `syncNotes()` caller bypassing `SyncGateChecker.isServerReachable()`; unreachable servers caused FATAL exceptions instead of a clean abort

**Add HTTP HEAD Check to Server Reachability Gate** ([13ad82e](https://github.com/inventory69/simple-notes-sync/commit/13ad82e))
- `SyncGateChecker` now performs a HEAD request after the TCP socket check to verify the server actually speaks HTTP — prevents false positives for servers with TLS issues

**Timestamp-Based Stale Sync State Detection** ([bd394fa](https://github.com/inventory69/simple-notes-sync/commit/bd394fa))
- `SyncStateManager` auto-resets `SYNCING` state older than 5 minutes; called from `Application.onCreate` and `MainViewModel.init` to cover both process death and configuration changes

**Add Logging to 18 Silent Catch Blocks** ([728f33a](https://github.com/inventory69/simple-notes-sync/commit/728f33a))
- Replace `catch (_: Exception)` with logged exceptions across `ImportWizard`, `WebDavSyncService`, `ConnectionManager`, `SyncGateChecker`, `ThemePreferences`, `MainViewModel`, `NoteDownloader`, and widget code

**Logging and User Hints for Silent Error Paths** ([e6dac28](https://github.com/inventory69/simple-notes-sync/commit/e6dac28))
- `WidgetConfig` now logs failures; import shows a user-visible hint when zero notes are imported despite candidates being present

**Add Logging to 403 Workaround in `SafeSardineWrapper.exists`** ([4917fc4](https://github.com/inventory69/simple-notes-sync/commit/4917fc4))
- Warns when the Jianguoyun 403-as-exists workaround triggers, so false positives are visible in debug logs

**Null Check for `sardine.getInputStream` in `readContent`** ([98a778f](https://github.com/inventory69/simple-notes-sync/commit/98a778f))
- Sardine can return null for non-existent resources; safe-call prevents NPE during import

**Resolve Deprecated APIs and Lint Warnings** ([6b87dd2](https://github.com/inventory69/simple-notes-sync/commit/6b87dd2))
- `LocalClipboardManager` → `LocalClipboard` + `ClipEntry`
- `Icons.Filled.PlaylistAdd` → `Icons.AutoMirrored.Filled.PlaylistAdd`
- `EncryptedSharedPreferences`/`MasterKey`: documented suppression (no replacement for Android 7+)
- All `SharedPreferences.edit().putX().apply()` chains converted to KTX `edit { }` blocks

**Prevent NPE in UrlValidator for Malformed URLs** ([1a3c6a7](https://github.com/inventory69/simple-notes-sync/commit/1a3c6a7))
- `parsedUrl.host` can be null for URLs like `http:///path`; safe-call with early return

**Replace Hardcoded German in Battery Optimization Fallback** ([98c39a6](https://github.com/inventory69/simple-notes-sync/commit/98c39a6))
- `ComposeSettingsActivity` now uses the existing `battery_optimization_open_settings_failed` string resource

**Widget Config Load Failure Hint** ([8d7d03f](https://github.com/inventory69/simple-notes-sync/commit/8d7d03f))
- The widget config activity now logs the fallback to defaults and shows a hint when previous config could not be loaded

### ✨ Improvements

**Migrate All Toast Messages to Material 3 Snackbar** ([a834e25](https://github.com/inventory69/simple-notes-sync/commit/a834e25))
- All 9 `Toast.makeText()` call sites replaced with ViewModel-driven Snackbar events for consistent UX
- `emitSnackbar()` helper added to `MainViewModel` and `NoteEditorViewModel`; `showToast()` extension deprecated

**Centralized Spacing Tokens** ([db4ce8f](https://github.com/inventory69/simple-notes-sync/commit/db4ce8f))
- Added `SpacingXSmall` (2dp), `SpacingMediumLarge` (12dp), `SpacingXXLarge` (32dp) to `Dimensions`; `MarkdownRenderer` migrated to centralized tokens

**Replace Hardcoded Legacy Path Filter with Named Constants** ([5f4d044](https://github.com/inventory69/simple-notes-sync/commit/5f4d044))
- Use `Constants.DEFAULT_SYNC_FOLDER_NAME` and `SyncUrlBuilder.MARKDOWN_SUFFIX` instead of magic strings in the root-fallback filter

**Extract `DEFAULT_OFFLINE_MODE` Constant** ([051054d](https://github.com/inventory69/simple-notes-sync/commit/051054d))
- Replace magic boolean in 3 `getBoolean()` calls; comment documents why default is `true` (safe for first install)

### 🔧 Internal Changes

- **StateFlow migration in `NoteEditorViewModel`** ([9071905](https://github.com/inventory69/simple-notes-sync/commit/9071905)) — `existingNote`, `isDirty`, `hasUnsavedChecklistEdits`, `isRestoringSnapshot` are now backed by `MutableStateFlow`
- **In-memory cache for `loadAllNotes` with 2s TTL** ([8b74b43](https://github.com/inventory69/simple-notes-sync/commit/8b74b43)) — avoids re-reading and parsing all JSON files on every onResume; race-safe via `AtomicLong` version counter
- **Replace `android.util.Log` with project Logger in 4 files** ([57c3246](https://github.com/inventory69/simple-notes-sync/commit/57c3246)) — `DragDropListState`, `NoteEditorScreen`, `ChecklistItemRow`, `SettingsViewModel`
- **Deduplicate `getTimeoutMs` into `ConnectionManager`** ([ea05e03](https://github.com/inventory69/simple-notes-sync/commit/ea05e03)) — `SyncGateChecker` now delegates
- **Extract `BatteryOptimizationHelper`** ([c3a1e3b](https://github.com/inventory69/simple-notes-sync/commit/c3a1e3b)) — `ComposeSettingsActivity` and `ComposeMainActivity` deduplicated; `setAutoSync()` only shows the dialog when not already exempt
- **Unify SharedPreferences/StateFlow write order** ([908493b](https://github.com/inventory69/simple-notes-sync/commit/908493b)) — write prefs first, then state; in-memory state never diverges from persisted state on partial failures
- **Extract `WidgetUpdateHelper`** ([00f66db](https://github.com/inventory69/simple-notes-sync/commit/00f66db)) — single helper for the `GlanceAppWidgetManager → getGlanceIds → forEach update` pattern
- **Document `@Immutable` on `Note`** ([15c6a12](https://github.com/inventory69/simple-notes-sync/commit/15c6a12))
- **Document empty product flavors** ([602c06a](https://github.com/inventory69/simple-notes-sync/commit/602c06a))
- **Remove redundant `viewBinding = false`** ([0e02124](https://github.com/inventory69/simple-notes-sync/commit/0e02124))
- **Extract widget magic numbers to named constants** ([2a84b32](https://github.com/inventory69/simple-notes-sync/commit/2a84b32))
- **Safe Long-to-Int coercion for socket timeout** ([b77ac4d](https://github.com/inventory69/simple-notes-sync/commit/b77ac4d))
- **Increase `MAX_LOG_ENTRIES` from 500 to 5000** ([b18fa8f](https://github.com/inventory69/simple-notes-sync/commit/b18fa8f)) — full sync cycles with 30+ notes can exceed 500 lines
- **Remove dead legacy `toReadableTime` without context param** ([d3ea343](https://github.com/inventory69/simple-notes-sync/commit/d3ea343))
- **Bump version 2.2.0 → 2.3.0** ([71cf215](https://github.com/inventory69/simple-notes-sync/commit/71cf215))

### 🌍 Translations

- Chinese (Simplified) updated via Weblate ([1be36bb](https://github.com/inventory69/simple-notes-sync/commit/1be36bb)) — thanks to [@heretic43](https://github.com/heretic43)!

Translation hosting generously provided by [Weblate](https://hosted.weblate.org/projects/simple-notes-sync/) — thank you for sponsoring open-source projects! 🙏

### 🙏 Acknowledgements

- [@angeld-jr2](https://github.com/angeld-jr2) — reported the backup-validation crash and provided the debug log that made the phantom-notes bug diagnosable
- [@Ichigo-Meow](https://github.com/Ichigo-Meow) — reported the MKCOL/WebDAV validation issue ([#55](https://github.com/inventory69/simple-notes-sync/issues/55))
- [@minosimo](https://github.com/minosimo) — reported the markdown-auto-sync regression ([#50](https://github.com/inventory69/simple-notes-sync/issues/50))
- MrsMinchen — contributed the widget checklist sort fix
- freemen — reported the missing auto-save on checklist sort changes

---

## [2.2.0] - 2026-03-30

### ✨ New Features

**Share Intent: Receive Text as New Note or Checklist** ([766f67e](https://github.com/inventory69/simple-notes-sync/commit/766f67e))
- Receive text/plain share intents from other apps as new notes or checklists
- Thanks to [@madelgijs](https://github.com/madelgijs) for the feature request! ([Discussion #46](https://github.com/inventory69/simple-notes-sync/discussions/46))

**New-Note Shortcut Widget** ([5c79ab6](https://github.com/inventory69/simple-notes-sync/commit/5c79ab6))
- Homescreen widget with auto-layout for quickly creating new notes
- Thanks to [@Stowaway2979](https://github.com/Stowaway2979) for the feature request! ([Discussion #49](https://github.com/inventory69/simple-notes-sync/discussions/49))

**Checklist Button in Markdown Toolbar** ([2157a09](https://github.com/inventory69/simple-notes-sync/commit/2157a09))
- Insert checklist items directly from the markdown editor toolbar

**Checklist Item Context Menu: Copy, Duplicate, Copy-to-Checklist** ([d98edd7](https://github.com/inventory69/simple-notes-sync/commit/d98edd7))
- Focused MoreVert menu on checklist items with copy text, duplicate item, and copy item to another checklist
- Thanks to freemen for the feature request!

**Auto-Collapse Expanded Items on Drag** ([c030794](https://github.com/inventory69/simple-notes-sync/commit/c030794))
- Expanded checklist items automatically collapse when drag-and-drop starts

### 🐛 Bug Fixes

**Fix Checklist Title Corruption from Missing Blank Line** ([c6cd50e](https://github.com/inventory69/simple-notes-sync/commit/c6cd50e))
- **Critical fix:** `toMarkdown()` was missing a blank line between `# Title` and the first checklist item, causing progressive title corruption on every sync cycle (first item merged into title)
- Defensive parsing in `fromMarkdown()` and `fromJson()` now detects and repairs corrupted titles
- One-time migration repairs all locally stored corrupted checklist notes on first start after update
- CRLF line ending normalization in Markdown parser prevents parse failures on Windows-edited files
- Corruption warning logging in Markdown sync import for monitoring
- Thanks to freemen for helping track down the corruption cascade!

**WiFi Sync WorkManager Fallback** ([ee0b54c](https://github.com/inventory69/simple-notes-sync/commit/ee0b54c))
- Added connectivity-change WorkManager fallback for reliable WiFi-triggered sync

### 🌍 Translations

- Chinese (Simplified) updated via Weblate ([9bcd4db](https://github.com/inventory69/simple-notes-sync/commit/9bcd4db), [864b23e](https://github.com/inventory69/simple-notes-sync/commit/864b23e))

### 📦 Code Quality

- APK packaging optimized: removed bundled kotlin_builtins and LICENSE files (−28 KB)

---

## [2.1.0] - 2026-03-26

### 🐛 Bug Fixes & UX Improvements

**Editor Toolbar Scaling for Narrow Displays** ([9b6ee8a](https://github.com/inventory69/simple-notes-sync/commit/9b6ee8a))
- Adaptive toolbar: on wide displays full titles + Undo/Redo in toolbar; on narrow displays / large font scale: shortened titles ("Edit", "New") + Undo/Redo in overflow menu
- Prevents text wrapping on small screens and high accessibility font scales
- Thanks to [@xdpirate](https://github.com/xdpirate) for reporting! ([#48](https://github.com/inventory69/simple-notes-sync/issues/48))

**Markdown Preview as Default View** ([f8b15a5](https://github.com/inventory69/simple-notes-sync/commit/f8b15a5))
- Existing text notes now open in Markdown preview mode by default
- New notes still open in edit mode with auto-keyboard focus
- Thanks to [@james0336](https://github.com/james0336), [@isawaway](https://github.com/isawaway), and MrsMinchen for the suggestion!

### 📝 Documentation & Metadata

**License Metadata Corrected** ([89667d1](https://github.com/inventory69/simple-notes-sync/commit/89667d1))
- Fixed remaining Apache 2.0 references in F-Droid changelogs (versionCode 27) — now all correctly show AGPL v3

**Issue Templates Simplified** ([5a56cf4](https://github.com/inventory69/simple-notes-sync/commit/5a56cf4))
- Bug report and question templates rewritten: English-only, fewer required fields, cleaner layout
- Thanks to [@xdpirate](https://github.com/xdpirate) for the feedback! ([#48](https://github.com/inventory69/simple-notes-sync/issues/48))

**Upcoming Features v2.2.0** ([5e9168b](https://github.com/inventory69/simple-notes-sync/commit/5e9168b))
- Added Share Intent ([Discussion #46](https://github.com/inventory69/simple-notes-sync/discussions/46) by [@madelgijs](https://github.com/madelgijs)), New Note Shortcut Widget ([Discussion #49](https://github.com/inventory69/simple-notes-sync/discussions/49) by [@Stowaway2979](https://github.com/Stowaway2979)), Markdown checklist button, and checklist item copy/duplicate to the roadmap

### 📦 Code Quality

**Detekt / Lint / ProGuard Audit** ([b9e6782](https://github.com/inventory69/simple-notes-sync/commit/b9e6782))
- Full code quality audit: detekt, lint, ktlint, ProGuard, and unit tests verified clean after all v2.1.0 changes

---

## [2.0.0] - 2026-03-20

### 🎨 Complete Compose Rewrite, Multi-Theme System & Architecture Overhaul

Major release: full migration to Jetpack Compose, removal of all legacy View-based code (~2,300 lines deleted), complete WebDavSyncService refactoring into focused modules, multi-theme system with 7 color schemes and animated transitions, Material 3 shared axis navigation, checklist drag-and-drop rewrite, comprehensive sync reliability fixes, and modernized dependencies.

### ✨ New Features

**Multi-Theme System with Animated Transitions and Tinted Surfaces** ([315c0a5](https://github.com/inventory69/simple-notes-sync/commit/315c0a5))
- New ThemeMode selector: System, Light, Dark, AMOLED
- 7 color schemes: Default, Blue, Green, Red, Purple, Orange, Dynamic (Material You on API 31+)
- Live preview with color swatches in Display Settings
- Crossfade theme transitions (500 ms) — smooth even in debug builds
- Tinted surface palettes for all color schemes (note cards match selected theme)
- Status bar and navigation bar colors sync at runtime on theme change

**Grid Column Scaling Control** ([3d4c2e0](https://github.com/inventory69/simple-notes-sync/commit/3d4c2e0))
- Toggle between automatic grid scaling (adaptive 150dp) and fixed column count
- Manual column count 1–5 via chip selector with mini-grid preview
- Section only visible when grid mode is active
- Included in backup/restore

**Undo/Redo Promoted to TopAppBar** ([75edf00](https://github.com/inventory69/simple-notes-sync/commit/75edf00))
- Undo and Redo moved from overflow menu to direct TopAppBar actions
- Buttons respect canUndo/canRedo state and appear dimmed when unavailable

**Display Mode Chip Selector** ([046f325](https://github.com/inventory69/simple-notes-sync/commit/046f325))
- Replace radio group with icon-above-label chips (consistent with theme/color selectors)
- Settings subtitle shows display mode, theme mode, and color theme (e.g. "List View · Dark · Default")

**Full App Settings Backup/Restore** ([4d07c11](https://github.com/inventory69/simple-notes-sync/commit/4d07c11))
- Backup/restore now includes all settings: server, sync, markdown, display, notes behaviour, notifications

**Autosave Status in Display Settings** ([92da701](https://github.com/inventory69/simple-notes-sync/commit/92da701))
- Display settings subtitle shows autosave status instead of theme info

**Disable-Logging-After-Export Dialog** ([2525a85](https://github.com/inventory69/simple-notes-sync/commit/2525a85))
- After sharing debug logs, a dialog asks whether to disable file logging

**Material 3 Shared Axis Transitions** ([3f5d19d](https://github.com/inventory69/simple-notes-sync/commit/3f5d19d), [365b0dd](https://github.com/inventory69/simple-notes-sync/commit/365b0dd))
- Horizontal shared axis (slide + fade) transitions for all navigation
- Consistent animations for both back-arrow and swipe-gesture on API 34+

### 🐛 Bug Fixes

**Checklist Drag-and-Drop Rewrite** ([89cc9a6](https://github.com/inventory69/simple-notes-sync/commit/89cc9a6))
- Complete rewrite of DragDropListState (~200 → 797 lines) fixing runaway auto-scroll, index desync during separator crossings, concurrent swap race conditions, and swap oscillation at viewport edges
- Key-based item tracking, continuous auto-scroll loop with mutex-locked swaps, anti-flapping guard, viewport-safety checks

**Offline Server Deletions Queued** ([1a5c889](https://github.com/inventory69/simple-notes-sync/commit/1a5c889))
- Notes deleted with "Delete everywhere" while server unreachable are now queued and processed on next sync instead of silently lost

**WebDAV 403 Compatibility** ([9523733](https://github.com/inventory69/simple-notes-sync/commit/9523733))
- Jianguoyun WebDAV returns 403 for HEAD on collections — now correctly treated as "exists" instead of "not found". Thanks [@james0336](https://github.com/james0336) for reporting!

**Thread-Safety and Resource Leaks** ([2ab04d1](https://github.com/inventory69/simple-notes-sync/commit/2ab04d1), [3c31f61](https://github.com/inventory69/simple-notes-sync/commit/3c31f61))
- Fix Activity leaks in NetworkMonitor, migrate syncStatus to StateFlow, add fileLock to Logger, eliminate race conditions in MainViewModel with StateFlow.update{}
- Close all InputStream/connection leaks in sardine calls, move file I/O off main thread, fix HttpURLConnection leak in server status check

**Sync Reliability** ([ec4bb1c](https://github.com/inventory69/simple-notes-sync/commit/ec4bb1c), [3d02118](https://github.com/inventory69/simple-notes-sync/commit/3d02118), [150543c](https://github.com/inventory69/simple-notes-sync/commit/150543c))
- Fix onResume sync throttle surviving process restarts (now in-memory)
- Prevent spurious sync after package update (race condition in NetworkMonitor)
- Skip auto-sync on resume from editor and settings

**Layout Scaling for Small Screens and Large Fonts** ([ef38a0e](https://github.com/inventory69/simple-notes-sync/commit/ef38a0e))
- Responsive fixes: scrollable dialogs, FilterChipRow column layout, adaptive grid threshold reduced 180→150dp. Danke an Mama <3

**Empty Plural Strings in de/tr/uk** ([2db80a4](https://github.com/inventory69/simple-notes-sync/commit/2db80a4))
- Fill missing plural forms for time-ago strings — timestamps were invisible on note cards in German, Turkish, Ukrainian

**Language Selector on API 33+** ([f54ef49](https://github.com/inventory69/simple-notes-sync/commit/f54ef49))
- Redirect to native Per-App Language Settings on API 33+ instead of in-app selector (eliminates activity-recreate flash)

**Save-on-Back Race Condition** ([afbef19](https://github.com/inventory69/simple-notes-sync/commit/afbef19))
- Flush TextFieldState and save in onPause to prevent data loss on back navigation

**Banner Color Flash** ([9b7a285](https://github.com/inventory69/simple-notes-sync/commit/9b7a285))
- Freeze last visible banner colors during dismiss animation to prevent wrong-color flash

**Splash Screen Flash** ([a5e0899](https://github.com/inventory69/simple-notes-sync/commit/a5e0899))
- Keep splash screen visible until notes are loaded — no more empty screen flash on cold start

**FAB Scrim Transition Artifact** ([833c30e](https://github.com/inventory69/simple-notes-sync/commit/833c30e), [2f75467](https://github.com/inventory69/simple-notes-sync/commit/2f75467))
- Snap FAB scrim to invisible before activity transition capture, restore fade-out animation

**Snackbar in Settings** ([24c62b8](https://github.com/inventory69/simple-notes-sync/commit/24c62b8), [0c76087](https://github.com/inventory69/simple-notes-sync/commit/0c76087))
- Replace unreliable Toast with Snackbar in all settings screens, show above keyboard

### 🏗️ Architecture & Refactoring

**WebDavSyncService Split** ([e0abff4](https://github.com/inventory69/simple-notes-sync/commit/e0abff4) → [7f467d7](https://github.com/inventory69/simple-notes-sync/commit/7f467d7))
- Split monolithic 2,735-line WebDavSyncService into 8 focused modules: SyncGateChecker, ETagCache, SyncTimestampManager, SyncExceptionMapper, SyncUrlBuilder, ConnectionManager, NoteUploader, NoteDownloader, MarkdownSyncManager (8 commits, 15→22/22)

**Legacy Code Removal** ([901ca77](https://github.com/inventory69/simple-notes-sync/commit/901ca77), [39b6e9f](https://github.com/inventory69/simple-notes-sync/commit/39b6e9f), [fb64a31](https://github.com/inventory69/simple-notes-sync/commit/fb64a31))
- Delete SettingsActivity (1,072 lines), MainActivity (857 lines), NoteEditorActivity (344 lines), all XML layouts/menus/drawables
- Compose Activities are now the sole implementations

**Modernization** ([dfd3b33](https://github.com/inventory69/simple-notes-sync/commit/dfd3b33), [ad137c3](https://github.com/inventory69/simple-notes-sync/commit/ad137c3), [65b6a26](https://github.com/inventory69/simple-notes-sync/commit/65b6a26), [9e547d2](https://github.com/inventory69/simple-notes-sync/commit/9e547d2), [07a7502](https://github.com/inventory69/simple-notes-sync/commit/07a7502))
- Replace LocalBroadcastManager with SharedFlow, migrate to viewModelFactory DSL, replace overridePendingTransition with ActivityOptions (API 34+), remove all @Suppress("DEPRECATION"), replace AlertDialog.Builder with Compose AlertDialog

### 📦 Dependencies & Build

**Dependency Updates** ([b57512f](https://github.com/inventory69/simple-notes-sync/commit/b57512f))
- Kotlin 2.0.21 → 2.1.0, Compose BOM 2026.01 → 2026.03, lifecycle 2.7 → 2.8.7, coroutines 1.7 → 1.9, navigation 2.7 → 2.8.5, activity 1.8 → 1.9.3, and 15+ more

**Code Quality** ([cf95fcd](https://github.com/inventory69/simple-notes-sync/commit/cf95fcd), [3933df0](https://github.com/inventory69/simple-notes-sync/commit/3933df0))
- Resolve all lint and detekt warnings, 500 unused strings deleted, ktlint formatting across 91 files, Kotlin 2.3.20

**APK Size Optimization** ([ce86b68](https://github.com/inventory69/simple-notes-sync/commit/ce86b68))
- Tighten R8/ProGuard keep rules — APK size 5.4 MB → 5.2 MB (-200 KB, classes.dex -507 KB)

**CI/CD** ([c6a3f25](https://github.com/inventory69/simple-notes-sync/commit/c6a3f25))
- Tag-based GitHub Actions release workflow for draft releases

### 📄 License

**License Change** ([6baaeda](https://github.com/inventory69/simple-notes-sync/commit/6baaeda))
- Changed from MIT to AGPL v3

---

## [1.12.0] - 2026-03-12

### 🌍 i18n, Settings Polish & Community

Focused release adding Chinese (Simplified) localization, proper plural forms for time-ago strings, notification status display in the settings overview, and Liberapay donation integration in the README.

### 🌍 Translations

**Chinese (Simplified) Translation** ([15a49d6](https://github.com/inventory69/simple-notes-sync/commit/15a49d6))
- Complete zh-CN localization (547 strings) based on Weblate contribution from [@heretic43](https://github.com/heretic43) ([#40](https://github.com/inventory69/simple-notes-sync/issues/40))
- App name kept as "Simple Notes" (untranslated)

### 🐛 Bug Fixes

**Proper i18n Plural Forms for Time-Ago Strings** ([c804964](https://github.com/inventory69/simple-notes-sync/commit/c804964))
- Converted `time_minutes_ago`, `time_hours_ago` and `time_days_ago` from `<string>` to `<plurals>` for proper grammatical forms in languages like Ukrainian
- Updated `Extensions.kt` to use `getQuantityString` accordingly
- All existing languages updated (en/de/tr/uk/zh)

### ✨ New Features

**Notification Status in Settings Overview** ([5d82e7d](https://github.com/inventory69/simple-notes-sync/commit/5d82e7d))
- The Sync & Notifications card on the settings hub screen now shows the current notification status alongside the trigger count
- Three states displayed: "Notifications enabled", "Errors and warnings only", "Notifications disabled"
- Consistent with the summary style of other settings cards

### 📝 Documentation

**Liberapay Donation Integration** ([7081286](https://github.com/inventory69/simple-notes-sync/commit/7081286))
- Added Shields.io donation badge to the badge row in README.md and README.de.md
- Added official Liberapay donate button in the Contributing section
- Both English and German READMEs updated

---

## [1.11.0] - 2026-03-10

### 🔔 Notifications, FAB Polish & Checklist Sorting

Focused release adding configurable notification settings with Android 13+ permission handling, ascending and descending sort-by-creation-date for checklists, a redesigned FAB overlay with animated scrim and unified action pills, restructured sync settings, hidden developer options, and several bug fixes around autosave behaviour and sync counting.

### ✨ New Features

**Notification Settings with Permission Handling** ([c1f5078](https://github.com/inventory69/simple-notes-sync/commit/c1f5078))
- Three new toggles in Sync & Notifications settings: global enable, errors-only mode, server unreachable warning
- Full Android 13+ POST_NOTIFICATIONS permission flow: request on first enable, rationale dialog on deny, app-settings redirect on permanent deny
- Permission state re-checked on every screen resume — toggle syncs with system state

**Sort Checklists by Creation Date (Ascending + Descending)** ([a265e3c](https://github.com/inventory69/simple-notes-sync/commit/a265e3c))
- New sort option "Date created ↑" (oldest first) and "Date created ↓" (newest first)
- Each checklist item gets a `createdAt` timestamp, preserved across edits and syncs
- Backward compatible: old items without timestamp use index as fallback
- Separator between unchecked/checked groups maintained for both directions

**Developer Options Hidden Behind Easter Egg** ([186a345](https://github.com/inventory69/simple-notes-sync/commit/186a345))
- Debug & Diagnostics settings hidden by default
- Unlock by tapping the app info card 5× in About screen (Android developer-options style)
- Session-only: resets on app restart, not persisted to SharedPreferences
- Countdown toast for last 2 taps, unlock confirmation toast

### 🐛 Bug Fixes

**Autosave Triggered for Empty Checklist Items** ([f7e25db](https://github.com/inventory69/simple-notes-sync/commit/f7e25db))
- Adding or deleting empty checklist items no longer triggers autosave
- No-change guard in `performSave()` and `saveOnBack()` skips save when only empty items differ from saved state
- Autosave indicator no longer misleadingly appears after adding an empty item

**Markdown Auto-Sync Double-Counting** ([7dd092e](https://github.com/inventory69/simple-notes-sync/commit/7dd092e))
- Creating a note with markdown auto-sync enabled no longer shows "2 notes synced" instead of 1
- Exported markdown files excluded from re-import in the same sync cycle via note ID tracking
- Technical re-uploads after markdown import no longer inflated the sync count
- `null` vs `emptyList()` mismatch in checklist content comparison fixed

**Misleading "Importing Markdown" Banner When No Files Changed** ([e352ce1](https://github.com/inventory69/simple-notes-sync/commit/e352ce1))
- Progress banner no longer shows a filename (e.g. "Test Neu.md") when 0 files are actually imported
- Fast-path skips IMPORTING_MARKDOWN phase entirely when all server files are unchanged
- Filename in progress update now only shown when a file is actually being downloaded

### 🎨 UI Improvements

**Animated FAB Scrim and Improved Colors** ([4a8a202](https://github.com/inventory69/simple-notes-sync/commit/4a8a202))
- FAB overlay now covers the entire screen including status bar (moved out of Scaffold)
- Semi-transparent animated scrim (50% black, 250 ms tween) replaces transparent dismiss overlay
- Sub-action colours changed from `secondaryContainer` to `primaryContainer` for better hierarchy

**Unified Action Pills** ([847154f](https://github.com/inventory69/simple-notes-sync/commit/847154f))
- Label text and icon combined into a single wide pill per action (Aegis Authenticator style)
- Entire pill is one click target — no more separate label + icon FAB
- Uses `surfaceContainerHigh` / `onSurface` for consistent appearance in dark mode

**Sync Settings Restructured** ([e9d48cb](https://github.com/inventory69/simple-notes-sync/commit/e9d48cb))
- Screen renamed from "Sync Settings" to "Sync & Notifications"
- Section header renamed from "Network & Performance" to "Network"
- Sub-headers "Instant Sync" and "Background Sync" removed for cleaner layout
- Sync triggers, network, and notifications extracted into separate composables for recomposition isolation

**Smooth Fade Transitions in Settings Navigation** ([1ef565d](https://github.com/inventory69/simple-notes-sync/commit/1ef565d))
- All settings routes now use a 700 ms fade transition instead of the abrupt default crossfade
- Defined globally on NavHost level — no per-route duplication

### 🌍 Translations

Thanks to [@FromKaniv](https://github.com/FromKaniv) for the Ukrainian translation and [@ksuheyl](https://github.com/ksuheyl) for the Turkish translation!

---

## [1.10.0] - 2026-03-01

### ✏️ Editor Overhaul, Share / Export & Sync Reliability

Major release adding PDF export, text and calendar sharing, a redesigned FAB menu, delete-from-editor with undo, batch server deletion with progress, adaptive tablet layouts, WorkManager reliability improvements, and real determinate progress bars for all sync phases.

### ✨ New Features

**Share & Export from Editor** ([e2b9f79](https://github.com/inventory69/simple-notes-sync/commit/e2b9f79), [2aca873](https://github.com/inventory69/simple-notes-sync/commit/2aca873), [57c4e96](https://github.com/inventory69/simple-notes-sync/commit/57c4e96)) _(Thanks to [@james0336](https://github.com/james0336) for requesting PDF export!)_
- New overflow menu (⋮) in the editor toolbar: Share as Text, Share as PDF, Export to Calendar
- PDF generated natively via `PdfDocument` API — no third-party library required
- Shared via `FileProvider` for secure, permission-free sharing with any PDF viewer
- Calendar export pre-fills title, all-day start date (today), and note content as description

**Expandable FAB Menu** ([85d68c4](https://github.com/inventory69/simple-notes-sync/commit/85d68c4), [61788e3](https://github.com/inventory69/simple-notes-sync/commit/61788e3))
- FAB replaced with an expanding speed-dial: tap `+` to reveal animated sub-action buttons for Text Note and Checklist
- `+` icon rotates to `×` when expanded; sub-actions slide in with staggered spring animation
- Transparent dismiss overlay closes the menu on outside tap
- Sub-action buttons and label pills use `secondaryContainer` colour, forming a visual unit
- Stronger shadow elevation (8–10 dp) ensures the FAB visually floats above note cards in both light and dark theme

**Delete Note from Editor with Undo** ([f3fd806](https://github.com/inventory69/simple-notes-sync/commit/f3fd806))
- Delete action moved from a blocking dialog to a bottom sheet confirmation
- After confirming, the editor closes and the main screen shows a timed undo snackbar
- Undo restores the note from the deleted state and cancels any scheduled server deletion

**Batch Server Deletion with Progress** ([39a873f](https://github.com/inventory69/simple-notes-sync/commit/39a873f))
- New `DELETING` sync phase shown in the banner when deleting multiple notes from the server
- Progress bar shows `current / total` with the current note title
- Phase transitions smoothly to `COMPLETED` with a result message

**Adaptive Layouts for Tablets & Landscape** ([a117cbe](https://github.com/inventory69/simple-notes-sync/commit/a117cbe))
- Editor content capped at 720 dp width, centred on wide screens
- Settings screens capped at 600 dp width, centred
- Main screen grid uses `Adaptive(180 dp)` columns — more columns appear automatically on tablets and in landscape
- Prepares for Android 16 (targetSdk 36) which ignores `screenOrientation` locks on displays ≥ 600 dp

**Undo/Redo in Note Editor** ([484bf3a](https://github.com/inventory69/simple-notes-sync/commit/484bf3a))
- Full undo/redo support for text notes and checklists via toolbar buttons
- Debounced snapshots: rapid keystrokes grouped into a single undo step (500 ms window)
- Stack limited to 50 entries; cleared on note switch to prevent cross-note undo
- Restoring a snapshot correctly updates the cursor position

**Configurable WebDAV Connection Timeout** ([b1aebc4](https://github.com/inventory69/simple-notes-sync/commit/b1aebc4))
- New Settings slider (1–30 s, default 8 s) to configure the WebDAV connection timeout
- Applied to all OkHttpClient instances (connect, read, write) used by Sardine
- Consistent, user-facing error messages for timeout, auth failure, not found, and server errors

**Markdown Auto-Sync Timeout Protection** ([7f74ae9](https://github.com/inventory69/simple-notes-sync/commit/7f74ae9))
- Enabling Markdown auto-sync now has a 10 s timeout for the initial export
- UI toggle updates optimistically and reverts if the export fails or times out
- Prevents the Settings screen from hanging on unreachable servers

**Save on Back Navigation** ([402382c](https://github.com/inventory69/simple-notes-sync/commit/402382c)) _(Thanks to [@GitNichtGibtsNicht](https://github.com/GitNichtGibtsNicht) for requesting autosave on back!)_
- Dirty notes are saved automatically when navigating back from the editor (system back + toolbar back)
- Only active when autosave is enabled; synchronous save without triggering sync
- Autosave toggle description updated to mention this behaviour

### 🐛 Bug Fixes

**Download Progress Always Indeterminate** ([c83aae3](https://github.com/inventory69/simple-notes-sync/commit/c83aae3))
- `ParallelDownloader` already tracked `completed / total` internally but `syncNotes()` hardcoded `total = 0`
- Fixed: `total` is now passed through → DOWNLOADING phase shows a real `LinearProgressIndicator`
- `importMarkdownFiles()` also reports per-file progress: banner shows `X / Y filename.md` with a determinate bar

**FGS Timeout on Android 15+** ([1e6eb64](https://github.com/inventory69/simple-notes-sync/commit/1e6eb64))
- Added `ensureActive()` checkpoints in `WebDavSyncService` download loop and markdown import so coroutines respond promptly to WorkManager cancellation on targetSdk 35+
- `CancellationException` handler in `SyncWorker` now logs the stop reason (API 31+)

**WorkManager Quota / Standby Stops Not Surfaced** ([3d66a19](https://github.com/inventory69/simple-notes-sync/commit/3d66a19))
- Added detailed stop reason logging: 16 WorkManager stop codes mapped to human-readable names
- When a sync is stopped due to JobScheduler quota or app standby, an info banner is shown next time the app comes to foreground

**Consistent Editor Overflow Menu Position** ([242ece3](https://github.com/inventory69/simple-notes-sync/commit/242ece3))
- Overflow menu (⋮) is now anchored to the `⋮` button in both text and checklist note types
- Previously the menu anchored to the outer actions `Row`, appearing too far left on checklist notes

**Markdown Import: Pre-Heading Content Lost** ([e33ac23](https://github.com/inventory69/simple-notes-sync/commit/e33ac23))
- Content before the first `#` heading was silently discarded during Markdown import
- Checklist detection improved: more item patterns are now recognised

**Checklist Autosave Not Triggered on Item Changes** ([5401df3](https://github.com/inventory69/simple-notes-sync/commit/5401df3))
- Deleting, adding, and reordering checklist items now correctly marks the note as dirty and triggers autosave

**Minimal Scroll When Adding New Checklist Items** ([c2fbe0b](https://github.com/inventory69/simple-notes-sync/commit/c2fbe0b))
- New checklist items scroll just enough to become visible instead of jumping to the top

**False Autosave on Checklist Cursor Tap** ([9ea7089](https://github.com/inventory69/simple-notes-sync/commit/9ea7089))
- Tapping a checklist item to position the cursor no longer triggers a false autosave
- No-op guards added to `updateChecklistItemText()` and `updateChecklistItemChecked()`

**Undo to Saved State Still Triggered Autosave** ([cf5027b](https://github.com/inventory69/simple-notes-sync/commit/cf5027b))
- Undoing all changes back to the last saved state now resets `isDirty` and cancels the pending autosave
- New `savedSnapshot` property captures state at load time and after every explicit save

**Foreign JSON Files Downloaded Unnecessarily** ([c409243](https://github.com/inventory69/simple-notes-sync/commit/c409243))
- Non-note JSON files (e.g. `google-services.json`) filtered before download via UUID format check

**Note Count Strings Not Pluralized Correctly** ([8ca8df3](https://github.com/inventory69/simple-notes-sync/commit/8ca8df3))
- Note count strings converted to proper Android plural forms (EN + DE)

### 🎨 UI Improvements

**Smooth Sync Banner Animations** ([c409243](https://github.com/inventory69/simple-notes-sync/commit/c409243))
- Banner enter: fadeIn (300 ms, EaseOutCubic) — no more abrupt push from top
- Banner exit: fadeOut + shrinkVertically (300/400 ms, EaseInCubic)
- Phase transitions use AnimatedContent crossfade (250 ms) for text changes
- Minimum display duration per active phase (400 ms) prevents unreadable flashes
- Auto-hide job decoupled from flow collector — guaranteed minimum display time

**Markdown Folder Mentioned in Sync Settings** ([a8bb80c](https://github.com/inventory69/simple-notes-sync/commit/a8bb80c))
- Sync folder hint now explicitly mentions the `notes-md/` subdirectory used for Markdown auto-sync

---

## [1.9.0] - 2026-02-25

### 🔄 Sync Quality, Performance & UI

Major release adding note filtering, markdown preview, configurable sync folder, opt-in autosave, widget polish, and significant sync improvements — server switch data loss fixed, parallel uploads, import wizard, and three sync edge-cases resolved.

### 🐛 Bug Fixes

**First Sync Fails if /notes/ Folder Doesn't Exist on Server** ([e012d17](https://github.com/inventory69/simple-notes-sync/commit/e012d17))
- First sync no longer fails silently when the `/notes/` directory hasn't been created on the server yet
- Root cause: `checkServerForChanges()` returned `false` (no changes) instead of `true` (proceed) when `lastSyncTime > 0` and folder was missing
- Fix: returns `true` to allow initial upload — server will create the folder on first PUT

**Server Switch Causes False "Deleted on Server" Status** ([0985209](https://github.com/inventory69/simple-notes-sync/commit/0985209))
- Switching to a new server no longer causes local notes to be falsely marked as deleted
- Root cause: E-Tag and content hash caches from the old server were not cleared, causing upload-skip to fire incorrectly — notes appeared SYNCED without being uploaded to the new server
- Fix: `clearServerCaches()` clears all E-Tag, content hash, last sync timestamp, and deletion tracker entries on server change
- `resetAllSyncStatusToPending()` now also resets DELETED_ON_SERVER status to PENDING

**Server Deletion Detection Guard Too Aggressive** ([56c0363](https://github.com/inventory69/simple-notes-sync/commit/56c0363))
- Users with 2–9 notes who deleted all from the Nextcloud web UI never got DELETED_ON_SERVER status
- Guard threshold raised from >1 to ≥10 to allow legitimate mass-deletion for small portfolios

**Parallel Markdown Export Race Condition** ([56c0363](https://github.com/inventory69/simple-notes-sync/commit/56c0363))
- Two notes with identical titles could overwrite each other's Markdown file during parallel upload
- Root cause: concurrent `exists()` → `put()` sequence without synchronization
- Fix: Markdown export serialized via Mutex (JSON uploads remain parallel)

**E-Tag Not Cached for "Local Newer" Download Skip** ([56c0363](https://github.com/inventory69/simple-notes-sync/commit/56c0363))
- When local note was newer than server version, the server E-Tag was not cached
- Caused unnecessary re-downloads on every subsequent sync
- Fix: E-Tag now saved in the else-branch of download result processing

**Tune Button Color Mismatch** ([135559a](https://github.com/inventory69/simple-notes-sync/commit/135559a))
- Untoggled tune button now uses default TopAppBar icon color instead of custom color

**Import Wizard Loses Checklist Content** ([5031848](https://github.com/inventory69/simple-notes-sync/commit/5031848))
- Checklist detection during Markdown import now preserves full note content

**Checklist Scroll Jump When Checking First Visible Item** ([8238af4](https://github.com/inventory69/simple-notes-sync/commit/8238af4))
- Checking the first visible checklist item no longer causes a scroll jump

**Checklist Original Order Lost After Insert/Delete** ([e601642](https://github.com/inventory69/simple-notes-sync/commit/e601642))
- Original item order is now cemented after insert/delete operations to prevent reordering glitches

**Inconsistent Scroll on Check/Un-Check** ([19dfb03](https://github.com/inventory69/simple-notes-sync/commit/19dfb03))
- Consistent scroll behavior when checking and unchecking checklist items

### ✨ New Features

**Notes Import Wizard** ([e012d17](https://github.com/inventory69/simple-notes-sync/commit/e012d17))
- New import screen in Settings — import notes from WebDAV server or local storage
- Supported formats: `.md` (with/without YAML frontmatter), `.json` (Simple Notes format or generic), `.txt` (plain text)
- WebDAV scan: recursive subfolder scan (depth 1), respects existing DeletionTracker entries
- Imported notes from YAML frontmatter or Simple Notes JSON are imported as SYNCED; others as PENDING
- Accessible via Settings → Import

**Parallel Uploads** ([187d338](https://github.com/inventory69/simple-notes-sync/commit/187d338))
- Notes are uploaded in parallel instead of sequentially — ~2× faster for multi-note changes
- Upload time for 4 notes reduced from ~11.5 s to ~6 s (measured on device)
- Second sync with unchanged notes: upload phase ~0 ms (all skipped via content hash)
- Bounded concurrency via Semaphore; file I/O writes serialized via Mutex
- New: `/notes-md/` existence check cached per sync run (saves ~480 ms × N exists() calls)

**Unified Parallel Connections Setting** ([ef200d0](https://github.com/inventory69/simple-notes-sync/commit/ef200d0))
- Parallel downloads (1/3/5/7/10) and uploads (hidden, max 6) merged into single "Parallel Connections" setting
- New options: 1, 3, 5 (reduced from 5 options — 7 and 10 removed since uploads cap at 6)
- Users with 7 or 10 selected are automatically migrated to 5
- Uploads capped at `min(setting, 6)` at runtime

**Filter Chip Row** ([952755f](https://github.com/inventory69/simple-notes-sync/commit/952755f), [71a0469](https://github.com/inventory69/simple-notes-sync/commit/71a0469), [07c41bb](https://github.com/inventory69/simple-notes-sync/commit/07c41bb))
- New filter bar below TopAppBar — filter notes by All / Text / Checklists
- Inline search field for quick note filtering by title
- Sort button moved from dialog into compact filter row icon
- Tune button in TopAppBar toggles filter row visibility

**Markdown Preview** ([e83a89a](https://github.com/inventory69/simple-notes-sync/commit/e83a89a))
- Live markdown preview for text notes with formatting toolbar
- Supports headings, bold, italic, strikethrough, lists, horizontal rules, code blocks
- Toggle between edit and preview mode

**Custom App Title** ([bf478c7](https://github.com/inventory69/simple-notes-sync/commit/bf478c7))
- Configurable app name in settings

**Configurable WebDAV Sync Folder** ([58cdf1e](https://github.com/inventory69/simple-notes-sync/commit/58cdf1e))
- Custom sync folder name (default: `notes`, configurable for multi-app setups)

**Opt-in Autosave** ([5800183](https://github.com/inventory69/simple-notes-sync/commit/5800183))
- Autosave with debounce timer (3s after last edit, configurable in settings)
- Disabled by default, opt-in via Settings

**Scroll to Top After Manual Sync** ([4697e49](https://github.com/inventory69/simple-notes-sync/commit/4697e49))
- Notes list scrolls to top after completing a manual sync

### 🔄 Improvements

**Widget: Monet Tint in Translucent Background** ([0f5a734](https://github.com/inventory69/simple-notes-sync/commit/0f5a734))
- Monet dynamic color tint preserved in translucent widget backgrounds

**Widget: Options Bar Background Removed** ([5e3273a](https://github.com/inventory69/simple-notes-sync/commit/5e3273a))
- Options bar background removed for seamless widget integration

**Widget: Strikethrough for Completed Items** ([eb9db2e](https://github.com/inventory69/simple-notes-sync/commit/eb9db2e))
- Completed checklist items in widgets now show strikethrough styling

**Widget: Auto-Refresh on onStop** ([2443908](https://github.com/inventory69/simple-notes-sync/commit/2443908))
- Widgets automatically refresh when leaving the app (onStop lifecycle hook)

**Checklist: Un-Check Restores Original Position** ([188a0f6](https://github.com/inventory69/simple-notes-sync/commit/188a0f6))
- Un-checking an item restores it to its original position

**Sort Button: Compact Icon Button** ([a1bd15a](https://github.com/inventory69/simple-notes-sync/commit/a1bd15a))
- Replaced AssistChip with compact IconButton + SwapVert icon

### 🛠️ Internal

**Code Quality** ([6708156](https://github.com/inventory69/simple-notes-sync/commit/6708156))
- Fixed deprecated `Icons.Outlined.Notes` → `Icons.AutoMirrored.Outlined.Notes`
- Removed unused `Color` import from ServerSettingsScreen + detekt baseline entry
- Logger timestamps use `Locale.ROOT` instead of `Locale.getDefault()`
- Removed obsolete `Build.VERSION_CODES.N` check (minSdk=24)

**Detekt Compliance** ([f0e143c](https://github.com/inventory69/simple-notes-sync/commit/f0e143c))
- Extraction of `ALL_DELETED_GUARD_THRESHOLD` constant for magic number compliance

**ProGuard/R8 Verification**
- Release build verified — no rule changes needed for v1.9.0

**Image Support Deferred to v2.0.0** ([845ba03](https://github.com/inventory69/simple-notes-sync/commit/845ba03))
- Local image embedding removed from v1.9.0 scope
- Feature preserved as v2.0.0 specification with full architecture proposal

**Weblate PR Workflow** ([efd782f](https://github.com/inventory69/simple-notes-sync/commit/efd782f))
- Weblate integration switched to PR-based translation workflow

**Documentation** ([395d154](https://github.com/inventory69/simple-notes-sync/commit/395d154))
- Documentation updated for v1.8.2 and v1.9.0 (FEATURES, UPCOMING, QUICKSTART)
- Fixed broken links across docs (closes #22)

---

## [1.8.2] - 2026-02-16

### 🔧 Stability, Editor & Widget Improvements

Major stability release fixing 26 issues — sync deadlocks, data loss prevention, SSL certificates, markdown sync loop, silent download failures, editor UX improvements, widget polish, and APK size optimization.

### 🐛 Bug Fixes

**Sync Stuck in "Already in Progress"** *(IMPL_01)* ([a62ab78](https://github.com/inventory69/simple-notes-sync/commit/a62ab78))
- Fixed 5 code paths in SyncWorker where `tryStartSync()` was called but state was never reset
- Early returns (no changes, gate blocked, server unreachable) now call `SyncStateManager.reset()`
- CancellationException handler now resets state instead of leaving it in SYNCING
- Generic Exception handler now calls `markError()` to properly transition state
- Root cause: SyncStateManager stayed in SYNCING state permanently, blocking all future syncs

**Self-Signed SSL Certificates in Release Builds** *(IMPL_02)* ([b3f4915](https://github.com/inventory69/simple-notes-sync/commit/b3f4915))
- Added `<certificates src="user" />` to network security base config
- User-installed CA certificates now work in release builds (previously debug-only)
- Required for self-hosted WebDAV servers with self-signed SSL certificates

**Text Notes Not Scrollable in Medium Widgets** *(IMPL_04)* ([8429306](https://github.com/inventory69/simple-notes-sync/commit/8429306))
- Changed NARROW_MED and WIDE_MED widget size classes to use `TextNoteFullView` (scrollable)
- Previously used `TextNotePreview` which was truncated and non-scrollable
- 2x1 and 4x1 widgets now show scrollable text content
- Removed unused `TextNotePreview` function and related constants

**Keyboard Auto-Capitalization** *(IMPL_05)* ([d93b439](https://github.com/inventory69/simple-notes-sync/commit/d93b439))
- Title field now uses `KeyboardCapitalization.Words`
- Content field now uses `KeyboardCapitalization.Sentences`
- Checklist items now use `KeyboardCapitalization.Sentences`

**Documentation: Sort Option Naming** *(IMPL_06)* ([465bd9c](https://github.com/inventory69/simple-notes-sync/commit/465bd9c))
- Changed "color"/"Farbe" to "type"/"Typ" in README files
- Updated F-Droid metadata descriptions (de-DE and en-US)

**Keyboard Auto-Scroll for Text Notes** *(IMPL_07)* ([bc266b9](https://github.com/inventory69/simple-notes-sync/commit/bc266b9))
- Migrated TextNoteContent from `TextFieldValue` API to `TextFieldState` API
- Added external `scrollState` parameter to `OutlinedTextField`
- Auto-scrolls to cursor position when keyboard opens

**Checklist Scroll Jump When Typing** *(IMPL_10)* ([974ef13](https://github.com/inventory69/simple-notes-sync/commit/974ef13))
- Replaced faulty auto-scroll logic from v1.8.1 with viewport-aware scroll
- Only scrolls if item actually extends below viewport

**Checklist Visual Glitch During Fast Scrolling** *(IMPL_11)* ([82e8972](https://github.com/inventory69/simple-notes-sync/commit/82e8972))
- Added `isDragConfirmed` state to prevent accidental drag activation during scroll
- Scoped `animateItem()` to confirmed drag operations only
- Root cause: `Modifier.animateItem()` caused fade-in/out animations when items entered/left viewport

**Checklist Drag Interrupted at Separator** *(IMPL_26)* ([8828391](https://github.com/inventory69/simple-notes-sync/commit/8828391))
- Dragging a checklist item across the checked/unchecked separator no longer drops the item
- Item stays in active drag while its checked state toggles seamlessly
- Root cause: Separate `itemsIndexed` blocks destroyed Composition on boundary crossing — unified into single `items` block

**SyncMutex Deadlock via clearSessionCache() Exception** *(IMPL_13)* ([99f451b](https://github.com/inventory69/simple-notes-sync/commit/99f451b))
- Wrapped `clearSessionCache()` in try-catch inside `finally` block
- Prevents Mutex from staying locked when cache cleanup throws

**False Error Banner on Sync Cancellation** *(IMPL_14)* ([1c45680](https://github.com/inventory69/simple-notes-sync/commit/1c45680))
- CancellationException no longer shows error banner to user
- Removed duplicate state resets in SyncWorker catch blocks

**Socket Leak in isServerReachable()** *(IMPL_15)* ([fac54d7](https://github.com/inventory69/simple-notes-sync/commit/fac54d7))
- Socket now properly closed in all code paths (was leaking on successful connect)

**CancellationException Swallowed in ParallelDownloader** *(IMPL_16)* ([4c34746](https://github.com/inventory69/simple-notes-sync/commit/4c34746))
- CancellationException now re-thrown instead of caught and retried
- Prevents infinite retry loop when WorkManager cancels sync

**Checklist Data Loss on onResume** *(IMPL_17)* ([b436623](https://github.com/inventory69/simple-notes-sync/commit/b436623))
- Checklist edits now persist when returning from notification shade or app switcher
- Root cause: `onResume()` reloaded note from database, discarding unsaved in-memory changes

**Duplicate Stale-Sync Cleanup** *(IMPL_18)* ([71ae747](https://github.com/inventory69/simple-notes-sync/commit/71ae747))
- Removed copy-paste duplicate reset block in `SimpleNotesApplication.onCreate()`

**NotesStorage Shadow + Download Cancellation** *(IMPL_19)* ([ede429c](https://github.com/inventory69/simple-notes-sync/commit/ede429c), [50ae9d8](https://github.com/inventory69/simple-notes-sync/commit/50ae9d8))
- Removed shadow `NotesStorage` instance in `hasUnsyncedChanges()` (19a)
- Replaced `runBlocking` with `coroutineScope` in `downloadRemoteNotes()` for proper cancellation (19b)
- Added read timeout to OkHttpClient instances (19c)

**Silent Download Failures Reported as Success** *(IMPL_21)* ([371d5e3](https://github.com/inventory69/simple-notes-sync/commit/371d5e3))
- Download exceptions now propagate instead of being silently caught
- Sync correctly reports failure when downloads fail

**PENDING Notes Not Detected** *(IMPL_22)* ([20de019](https://github.com/inventory69/simple-notes-sync/commit/20de019))
- `hasUnsyncedChanges()` now checks for notes with PENDING sync status
- Fixes issue where switching servers left notes unsynced

**E-Tag/Timestamp Download Order** *(IMPL_23)* ([68dbb4e](https://github.com/inventory69/simple-notes-sync/commit/68dbb4e))
- E-Tag comparison now runs before timestamp check (was skipping changed notes)
- Fixes cross-device sync where timestamps matched but content differed

**Silent Sync Promote to Visible** *(IMPL_24)* ([940a494](https://github.com/inventory69/simple-notes-sync/commit/940a494))
- Pull-to-refresh during background sync now shows sync banner instead of "already in progress" error

**Markdown Sync Feedback Loop** *(IMPL_25)* ([74194d4](https://github.com/inventory69/simple-notes-sync/commit/74194d4))
- Fixed 5 root causes creating an infinite export→import→re-export cycle
- UUID normalization, server-mtime preservation, timezone-aware comparison, path sanitization, content-type-aware comparison

### ✨ New Features

**Enter-Key Navigation from Title to Content** *(IMPL_09)* ([81b9aca](https://github.com/inventory69/simple-notes-sync/commit/81b9aca))
- Title field is now single-line with `ImeAction.Next`
- Pressing Enter/Next jumps to content field or first checklist item

### 🔄 Improvements

**Widget Content Padding** *(IMPL_08)* ([2ae5ce5](https://github.com/inventory69/simple-notes-sync/commit/2ae5ce5))
- Unified padding for all widget views: 12dp horizontal, 4dp top, 12dp bottom

**Widget Entry Spacing** *(IMPL_12)* ([c3d4b33](https://github.com/inventory69/simple-notes-sync/commit/c3d4b33))
- Increased checklist and text widget spacing for better readability

**Sync State Timeout**
- Added 5-minute timeout for stale sync states in `SyncStateManager`
- `tryStartSync()` auto-resets if existing sync is older than 5 minutes

**Cold Start State Cleanup**
- `SimpleNotesApplication.onCreate()` now resets orphaned SYNCING state

**APK Size Optimization** *(IMPL_03)* ([7867894](https://github.com/inventory69/simple-notes-sync/commit/7867894))
- Replaced broad ProGuard rule with granular rules — keeps only what reflection actually needs

**Version Bump**
- versionCode: 21 → 22
- versionName: 1.8.1 → 1.8.2

---

## [1.8.1] - 2026-02-11

### 🛠️ Bugfix & Polish Release

Checklist fixes, widget improvements, sync hardening, and code quality cleanup.

### 🐛 Bug Fixes

**Checklist Sort Persistence** ([7dbc06d](https://github.com/inventory69/simple-notes-sync/commit/7dbc06d))
- Fixed sort option not applied when reopening a checklist
- Root cause: `sortChecklistItems()` always sorted unchecked-first instead of reading `_lastChecklistSortOption`
- Now correctly restores all sort modes (Manual, Alphabetical, Unchecked/Checked First)

**Widget Scroll on Standard Size** ([c72b3fe](https://github.com/inventory69/simple-notes-sync/commit/c72b3fe))
- Fixed scroll not working on standard 3×2 widget size (110–150dp height)
- Added `NARROW_SCROLL` and `WIDE_SCROLL` size classes with 150dp threshold
- Removed `clickable` modifier from unlocked checklists to enable scrolling

**Auto-Sync Toast Removed** ([fe6935a](https://github.com/inventory69/simple-notes-sync/commit/fe6935a))
- Removed unexpected toast notification on automatic background sync
- Silent auto-sync stays silent; only errors are shown

**Gradient & Drag Regression** ([24fe32a](https://github.com/inventory69/simple-notes-sync/commit/24fe32a))
- Fixed gradient overlay regression on long checklist items
- Fixed drag-and-drop flicker when moving items between boundaries

### 🆕 New Features

**Widget Checklist Sorting & Separators** ([66d98c0](https://github.com/inventory69/simple-notes-sync/commit/66d98c0))
- Widgets now apply saved sort option from the editor
- Visual separator between unchecked/checked items (MANUAL & UNCHECKED_FIRST modes)
- Auto-sort when toggling checkboxes in the widget
- Changed ✅ → ☑️ emoji for checked items

**Checklist Preview Sorting** ([2c43b47](https://github.com/inventory69/simple-notes-sync/commit/2c43b47))
- Main screen preview (NoteCard, NoteCardCompact, NoteCardGrid) now respects saved sort option
- New `ChecklistPreviewHelper` with shared sorting logic

**Auto-Scroll on Line Wrap** ([3e4b1bd](https://github.com/inventory69/simple-notes-sync/commit/3e4b1bd))
- Checklist editor auto-scrolls when typing causes text to wrap to a new line
- Keeps cursor visible at bottom of list during editing

**Separator Drag Cross-Boundary** ([7b55811](https://github.com/inventory69/simple-notes-sync/commit/7b55811))
- Drag-and-drop now works across the checked/unchecked separator
- Items auto-toggle their checked state when dragged across boundaries
- Extracted `DraggableChecklistItem` composable for reusability

### 🔄 Improvements

**Sync Rate-Limiting & Battery Protection** ([ffe0e46](https://github.com/inventory69/simple-notes-sync/commit/ffe0e46), [a1a574a](https://github.com/inventory69/simple-notes-sync/commit/a1a574a))
- Global 30-second cooldown between sync operations (auto/WiFi/periodic)
- onSave syncs bypass global cooldown (retain own 5s throttle)
- New `SyncStateManager` singleton for centralized state tracking
- Prevents battery drain from rapid successive syncs

**Toast → Banner Migration** ([27e6b9d](https://github.com/inventory69/simple-notes-sync/commit/27e6b9d))
- All non-interactive notifications migrated to unified Banner system
- Server-delete results show as INFO/ERROR banners
- Added INFO phase to SyncPhase enum with auto-hide (2.5s)
- Snackbars with Undo actions remain unchanged

**ProGuard Rules Audit** ([6356173](https://github.com/inventory69/simple-notes-sync/commit/6356173))
- Added missing keep rules for Widget ActionCallback classes
- Added Compose-specific ProGuard rules
- Prevents ClassNotFoundException in release builds

### 🧹 Code Quality

**Detekt Compliance** ([1a6617a](https://github.com/inventory69/simple-notes-sync/commit/1a6617a))
- Resolved all 12 detekt findings (0 issues remaining)
- Refactored `NoteEditorViewModel.loadNote()` to reduce nesting depth
- Extracted constants for magic numbers in editor
- Removed unused imports from `UpdateChangelogSheet`
- Set `maxIssues: 0` in detekt config

---

## [1.8.0] - 2026-02-10

### 🚨 CRITICAL BUGFIX (Tag recreated)

**R8/ProGuard Obfuscation Fix - Prevents Data Loss**
- 🔧 **CRITICAL:** Fixed incorrect ProGuard class path for `Note$Companion$NoteRaw`
  - Original v1.8.0 had specific `-keep` rules that didn't match
  - R8 obfuscated all NoteRaw fields (id→a, title→b, ...)
  - Gson couldn't parse JSON anymore → **ALL notes appeared lost**
  - Reverted to safe broad rule: `-keep class dev.dettmer.simplenotes.** { *; }`
- 🛡️ Added safety-guards in `detectServerDeletions()`
  - Prevents mass deletion when `serverNoteIds` is empty (network errors)
  - Aborts if ALL local notes would be marked as deleted
- ✅ Notes were never actually lost (JSON files intact on disk + server)
- ✅ Downgrade to v1.7.2 restored all notes

**⚠️ If you installed original v1.8.0:** Your notes are safe! Just update.

### 🎉 Major: Widgets, Sorting & Advanced Sync

Complete widget system with interactive checklists, note sorting, and major sync improvements!

### 🆕 Homescreen Widgets

**Full Jetpack Glance Widget Framework** ([539987f](https://github.com/inventory69/simple-notes-sync/commit/539987f))
- 5 responsive size classes (SMALL, NARROW_MED, NARROW_TALL, WIDE_MED, WIDE_TALL)
- Interactive checklist checkboxes that sync immediately to server
- Material You dynamic colors with configurable background opacity (0-100%)
- Lock widget toggle to prevent accidental edits
- Read-only mode with permanent options bar for locked widgets
- Widget configuration activity with note selection and settings
- Auto-refresh after sync completion
- Tap content to open editor (unlocked) or show options (locked)
- Complete resource cleanup fixes for connection leaks

**Widget State Management:**
- NoteWidgetState keys for per-instance persistence via DataStore
- Five top-level ActionCallbacks (Toggle Checkbox, Lock, Options, Refresh, Config)
- Type-safe parameter passing with NoteWidgetActionKeys

### 📊 Note & Checklist Sorting

**Note Sorting** ([96c819b](https://github.com/inventory69/simple-notes-sync/commit/96c819b))
- Sort by: Updated (newest/oldest), Created, Title (A-Z/Z-A), Type
- Persistent sort preferences (saved in SharedPreferences)
- Sort dialog in main screen with direction toggle
- Combined sortedNotes StateFlow in MainViewModel

**Checklist Sorting** ([96c819b](https://github.com/inventory69/simple-notes-sync/commit/96c819b), [900dad7](https://github.com/inventory69/simple-notes-sync/commit/900dad7))
- Sort by: Manual, Alphabetical, Unchecked First, Checked Last
- Visual separator between unchecked/checked items with count display
- Auto-sort on item toggle and reordering
- Drag-only within same group (unchecked/checked)
- Smooth fade/slide animations for item transitions
- Unit tested with 9 test cases for sorting logic validation

### 🔄 Sync Improvements

**Server Deletion Detection** ([40d7c83](https://github.com/inventory69/simple-notes-sync/commit/40d7c83), [bf7a74e](https://github.com/inventory69/simple-notes-sync/commit/bf7a74e))
- New `DELETED_ON_SERVER` sync status for multi-device scenarios
- Detects when notes are deleted on other clients
- Zero performance impact (uses existing PROPFIND data)
- Deletion count shown in sync banner: "3 synced · 2 deleted on server"
- Edited deleted notes automatically re-upload to server (status → PENDING)

**Sync Status Legend** ([07607fc](https://github.com/inventory69/simple-notes-sync/commit/07607fc))
- Help button (?) in main screen TopAppBar
- Dialog explaining all 5 sync status icons with descriptions
- Only visible when sync is configured

**Live Sync Progress UI** ([df37d2a](https://github.com/inventory69/simple-notes-sync/commit/df37d2a))
- Real-time phase indicators: PREPARING, UPLOADING, DOWNLOADING, IMPORTING_MARKDOWN
- Upload progress shows x/y counter (known total)
- Download progress shows count (unknown total)
- Single unified SyncProgressBanner (replaces dual system)
- Auto-hide: COMPLETED (2s), ERROR (4s)
- No misleading counters when nothing to sync
- Silent auto-sync stays silent, errors always shown

**Parallel Downloads** ([bdfc0bf](https://github.com/inventory69/simple-notes-sync/commit/bdfc0bf))
- Configurable concurrent downloads (default: 3 simultaneous)
- Kotlin coroutines async/awaitAll pattern
- Individual download timeout handling
- Graceful sequential fallback on concurrent errors
- Optimized network utilization for faster sync

### ✨ UX Improvements

**Checklist Enhancements:**
- Overflow gradient for long text items ([3462f93](https://github.com/inventory69/simple-notes-sync/commit/3462f93))
- Auto-expand on focus, collapse to 5 lines when unfocused
- Drag & Drop flicker fix with straddle-target-center detection ([538a705](https://github.com/inventory69/simple-notes-sync/commit/538a705))
- Adjacency filter prevents item jumps during fast drag
- Race-condition fix for scroll + move operations

**Settings UI Polish:**
- Smooth language switching without activity recreate ([881c0fd](https://github.com/inventory69/simple-notes-sync/commit/881c0fd))
- Grid view as default for new installations ([6858446](https://github.com/inventory69/simple-notes-sync/commit/6858446))
- Sync settings restructured into clear sections: Triggers & Performance ([eaac5a0](https://github.com/inventory69/simple-notes-sync/commit/eaac5a0))
- Changelog link added to About screen ([49810ff](https://github.com/inventory69/simple-notes-sync/commit/49810ff))

**Post-Update Changelog Dialog** ([661d9e0](https://github.com/inventory69/simple-notes-sync/commit/661d9e0))
- Shows localized changelog on first launch after update
- Material 3 ModalBottomSheet with slide-up animation
- Loads F-Droid changelogs via assets (single source of truth)
- One-time display per versionCode (stored in SharedPreferences)
- Clickable GitHub link for full changelog
- Dismissable via button or swipe gesture
- Test mode in Debug Settings with reset option

**Backup Settings Improvements** ([3e946ed](https://github.com/inventory69/simple-notes-sync/commit/3e946ed))
- New BackupProgressCard with LinearProgressIndicator
- 3-phase status system: In Progress → Completion → Clear
- Success status shown for 2s, errors for 3s
- Removed redundant toast messages
- Buttons stay visible and disabled during operations
- Exception logging for better error tracking

### 🐛 Bug Fixes

**Widget Text Display** ([d045d4d](https://github.com/inventory69/simple-notes-sync/commit/d045d4d))
- Fixed text notes showing only 3 lines in widgets
- Changed from paragraph-based to line-based rendering
- LazyColumn now properly scrolls through all content
- Empty lines preserved as 8dp spacers
- Preview limits increased: compact 100→120, full 200→300 chars

### 🔧 Code Quality

**Detekt Cleanup** ([1da1a63](https://github.com/inventory69/simple-notes-sync/commit/1da1a63))
- Resolved all 22 Detekt warnings
- Removed 7 unused imports
- Defined constants for 5 magic numbers
- Optimized state reads with derivedStateOf
- Build: 0 Lint errors + 0 Detekt warnings

### 📚 Documentation

- Complete implementation plans for all 23 v1.8.0 features
- Widget system architecture and state management docs
- Sorting logic unit tests with edge case coverage
- F-Droid changelogs (English + German)

---

## [1.7.2] - 2026-02-04

### 🐛 Critical Bug Fixes

#### JSON/Markdown Timestamp Sync

**Problem:** External editors (Obsidian, Typora, VS Code, custom editors) update Markdown content but don't update YAML `updated:` timestamp, causing the Android app to skip changes.

**Solution:**
- Server file modification time (`mtime`) is now used as source of truth instead of YAML timestamp
- Content changes detected via hash comparison
- Notes marked as `PENDING` after Markdown import → JSON automatically re-uploaded on next sync
- Fixes sorting issues after external edits

#### SyncStatus on Server Always PENDING

**Problem:** All JSON files on server contained `"syncStatus": "PENDING"` even after successful sync, confusing external clients.

**Solution:**
- Status is now set to `SYNCED` **before** JSON serialization
- Server and local copies are now consistent
- External web/Tauri editors can correctly interpret sync state

#### Deletion Tracker Race Condition

**Problem:** Batch deletes could lose deletion records due to concurrent file access.

**Solution:**
- Mutex-based synchronization for deletion tracking
- New `trackDeletionSafe()` function prevents race conditions
- Guarantees zombie note prevention even with rapid deletes

#### ISO8601 Timezone Parsing

**Problem:** Markdown imports failed with timezone offsets like `+01:00` or `-05:00`.

**Solution:**
- Multi-format ISO8601 parser with fallback chain
- Supports UTC (Z), timezone offsets (+01:00, +0100), and milliseconds
- Compatible with Obsidian, Typora, VS Code timestamps

### ⚡ Performance Improvements

#### E-Tag Batch Caching

- E-Tags are now written in single batch operation instead of N individual writes
- Performance gain: ~50-100ms per sync with multiple notes
- Reduced disk I/O operations

#### Memory Leak Prevention

- `SafeSardineWrapper` now implements `Closeable` for explicit resource cleanup
- HTTP connection pool is properly evicted after sync
- Prevents socket exhaustion during frequent syncs

### 🔧 Technical Details

- **IMPL_001:** `kotlinx.coroutines.sync.Mutex` for thread-safe deletion tracking
- **IMPL_002:** Pattern-based ISO8601 parser with 8 format variants
- **IMPL_003:** Connection pool eviction + dispatcher shutdown in `close()`
- **IMPL_004:** Batch `SharedPreferences.Editor` updates
- **IMPL_014:** Server `mtime` parameter in `Note.fromMarkdown()`
- **IMPL_015:** `syncStatus` set before `toJson()` call

### 📚 Documentation

- External Editor Specification for web/Tauri editor developers
- Detailed implementation documentation for all bugfixes

---

## [1.7.1] - 2026-02-02

### 🐛 Critical Bug Fixes

#### Android 9 App Crash Fix ([#15](https://github.com/inventory69/simple-notes-sync/issues/15))

**Problem:** App crashed on Android 9 (API 28) when using WorkManager Expedited Work for background sync.

**Root Cause:** When `setExpedited()` is used in WorkManager, the `CoroutineWorker` must implement `getForegroundInfo()` to return a Foreground Service notification. On Android 9-11, WorkManager calls this method, but the default implementation throws `IllegalStateException: Not implemented`.

**Solution:** Implemented `getForegroundInfo()` in `SyncWorker` to return a proper `ForegroundInfo` with sync progress notification.

**Details:**
- Added `ForegroundInfo` with sync progress notification for Android 9-11
- Android 10+: Sets `FOREGROUND_SERVICE_TYPE_DATA_SYNC` for proper service typing
- Added Foreground Service permissions to AndroidManifest.xml
- Notification shows sync progress with indeterminate progress bar
- Thanks to [@roughnecks](https://github.com/roughnecks) for the detailed debugging!

#### VPN Compatibility Fix ([#11](https://github.com/inventory69/simple-notes-sync/issues/11))

- WiFi socket binding now correctly detects Wireguard VPN interfaces (tun*, wg*, *-wg-*)
- Traffic routes through VPN tunnel instead of bypassing it directly to WiFi
- Fixes "Connection timeout" when syncing to external servers via VPN

### 🔧 Technical Changes

- New `SafeSardineWrapper` class ensures proper HTTP connection cleanup
- Reduced unnecessary 401 authentication challenges with preemptive auth headers
- Added ProGuard rule to suppress harmless TextInclusionStrategy warnings on older Android versions
- VPN interface detection via `NetworkInterface.getNetworkInterfaces()` pattern matching
- Foreground Service detection and notification system for background sync tasks

### 🌍 Localization

- Fixed hardcoded German error messages - now uses string resources for proper localization
- Added German and English strings for sync progress notifications

---

## [1.7.0] - 2026-01-26

### 🎉 Major: Grid View, WiFi-Only Sync & VPN Support

Pinterest-style grid, WiFi-only sync mode, and proper VPN support!

### 🎨 Grid Layout

- Pinterest-style staggered grid without gaps
- Consistent 12dp spacing between cards
- Scroll position preserved when returning from settings
- New unified `NoteCardGrid` with dynamic preview lines (3 small, 6 large)

### 📡 Sync Improvements

- **WiFi-only sync toggle** - Sync only when connected to WiFi
- **VPN support** - Sync works correctly when VPN is active (traffic routes through VPN)
- **Server change detection** - All notes reset to PENDING when server URL changes
- **Faster server check** - Socket timeout reduced from 2s to 1s
- **"Sync already running" feedback** - Shows snackbar when sync is in progress

### 🔒 Self-Signed SSL Support

- **Documentation added** - Guide for using self-signed certificates
- Uses Android's built-in CA trust store
- Works with ownCloud, Nextcloud, Synology, home servers

### 🔧 Technical

- `NoteCardGrid` component with dynamic maxLines
- Removed FullLine spans for gapless layout
- `resetAllSyncStatusToPending()` in NotesStorage
- VPN detection in `getOrCacheWiFiAddress()`

---

## [1.6.1] - 2026-01-20

### 🧹 Code Quality & Build Improvements

- **detekt: 0 issues** - All 29 code quality issues resolved
  - Trivial fixes: Unused imports, MaxLineLength
  - File rename: DragDropState.kt → DragDropListState.kt
  - MagicNumbers → Constants (Dimensions.kt, SyncConstants.kt)
  - SwallowedException: Logger.w() added for better error tracking
  - LongParameterList: ChecklistEditorCallbacks data class created
  - LongMethod: ServerSettingsScreen split into components
  - @Suppress annotations for legacy code (WebDavSyncService, SettingsActivity)

- **Zero build warnings** - All 21 deprecation warnings eliminated
  - File-level @Suppress for deprecated imports
  - ProgressDialog, LocalBroadcastManager, AbstractSavedStateViewModelFactory
  - onActivityResult, onRequestPermissionsResult
  - Gradle Compose config cleaned up (StrongSkipping is now default)

- **ktlint reactivated** - Linting re-enabled with Compose-specific rules
  - .editorconfig created with Compose formatting rules
  - Legacy files excluded: WebDavSyncService.kt, build.gradle.kts
  - ignoreFailures=true for gradual migration

- **CI/CD improvements** - GitHub Actions lint checks integrated
  - detekt + ktlint + Android Lint run before build in pr-build-check.yml
  - Ensures code quality on every pull request

### 🔧 Technical Improvements

- **Constants refactoring** - Better code organization
  - ui/theme/Dimensions.kt: UI-related constants
  - utils/SyncConstants.kt: Sync operation constants

- **Preparation for v2.0.0** - Legacy code marked for removal
  - SettingsActivity and MainActivity (replaced by Compose versions)
  - All deprecated APIs documented with removal plan

---

## [1.6.0] - 2026-01-19

### 🎉 Major: Configurable Sync Triggers

Fine-grained control over when your notes sync - choose which triggers fit your workflow best!

### ⚙️ Sync Trigger System

- **Individual trigger control** - Enable/disable each sync trigger separately in settings
- **5 Independent Triggers:**
  - **onSave Sync** - Sync immediately after saving a note (5s throttle)
  - **onResume Sync** - Sync when app is opened (60s throttle)
  - **WiFi-Connect Sync** - Sync when WiFi is connected
  - **Periodic Sync** - Background sync every 15/30/60 minutes (configurable)
  - **Boot Sync** - Start background sync after device restart

- **Smart Defaults** - Only event-driven triggers active by default (onSave, onResume, WiFi-Connect)
- **Battery Optimized** - ~0.2%/day with defaults, up to ~1.0% with periodic sync enabled
- **Offline Mode UI** - Grayed-out sync toggles when no server configured
- **Dynamic Settings Subtitle** - Shows count of active triggers on main settings screen

### 🔧 Server Configuration Improvements

- **Offline Mode Toggle** - Disable all network features with one switch
- **Split Protocol & Host** - Protocol (http/https) shown as non-editable prefix
- **Clickable Settings Cards** - Full card clickable for better UX
- **Clickable Toggle Rows** - Click text/icon to toggle switches (not just the switch itself)

### 🐛 Bug Fixes

- **Fixed:** Missing 5th sync trigger (Boot) in main settings screen subtitle count
- **Various fixes** - UI improvements and stability enhancements

### 🔧 Technical Improvements

- **Reactive offline mode state** - StateFlow ensures UI updates correctly
- **Separated server config checks** - `hasServerConfig()` vs `isServerConfigured()` (offline-aware)
- **Improved constants** - All sync trigger keys and defaults in Constants.kt
- **Better code organization** - Settings screens refactored for clarity

### Looking Ahead

> 🚀 **v1.7.0** will bring server folder checking and additional community features.
> Feature requests welcome as [GitHub Issue](https://github.com/inventory69/simple-notes-sync/issues).

---

## [1.5.0] - 2026-01-15

### 🎉 Major: Jetpack Compose UI Redesign

The complete UI has been migrated from XML Views to Jetpack Compose. The app is now more modern, faster, and smoother.

### 🌍 New Feature: Internationalization (i18n)

- **English language support** - All 400+ strings translated
- **Automatic language detection** - Follows system language
- **Manual language selection** - Switchable in settings
- **Per-App Language (Android 13+)** - Native language setting via system settings
- **locales_config.xml** - Complete Android integration

### ⚙️ Modernized Settings

- **7 categorized settings screens** - Clearer and more intuitive
- **Compose Navigation** - Smooth transitions between screens
- **Consistent design** - Material Design 3 throughout

### ✨ UI Improvements

- **Selection Mode** - Long-press for multi-select instead of swipe-to-delete
- **Batch Delete** - Delete multiple notes at once
- **Silent-Sync Mode** - No banner during auto-sync (only for manual sync)
- **App Icon in About Screen** - High-quality display
- **App Icon in Empty State** - Instead of emoji when note list is empty
- **Splash Screen Update** - Uses app foreground icon
- **Slide Animations** - Smooth animations in NoteEditor

### 🔧 Technical Improvements

- **Jetpack Compose** - Complete UI migration
- **Compose ViewModel Integration** - StateFlow for reactive UI
- **Improved Code Quality** - Detekt/Lint warnings fixed
- **Unused Imports Cleanup** - Cleaner codebase

### Looking Ahead

> 🚀 **v1.6.0** will bring server folder checking and further technical modernizations.
> Feature requests welcome as [GitHub Issue](https://github.com/inventory69/simple-notes-sync/issues).

---

## [1.4.1] - 2026-01-11

### Fixed

- **🗑️ Deleting older notes (v1.2.0 compatibility)**
  - Notes from app version v1.2.0 or earlier are now correctly deleted from the server
  - Fixes issue with multi-device usage with older notes

- **🔄 Checklist sync backward compatibility**
  - Checklists now also saved as text fallback in the `content` field
  - Older app versions (v1.3.x) display checklists as readable text
  - Format: GitHub-style task lists (`[ ] Item` / `[x] Item`)
  - Recovery mode: If checklist items are lost, they are recovered from content

### Improved

- **📝 Checklist auto line-wrap**
  - Long checklist texts now automatically wrap
  - No more limit to 3 lines
  - Enter key still creates a new item

### Looking Ahead

> 🚀 **v1.5.0** will be the next major release. We're collecting ideas and feedback!
> Feature requests welcome as [GitHub Issue](https://github.com/inventory69/simple-notes-sync/issues).

---

## [1.4.0] - 2026-01-10

### 🎉 New Feature: Checklists

- **✅ Checklist Notes**
  - New note type: Checklists with tap-to-toggle items
  - Add items via dedicated input field with "+" button
  - Drag & drop reordering (long-press to activate)
  - Swipe-to-delete items
  - Visual distinction: Checked items get strikethrough styling
  - Type selector when creating new notes (Text or Checklist)

- **📝 Markdown Integration**
  - Checklists export as GitHub-style task lists (`- [ ]` / `- [x]`)
  - Compatible with Obsidian, Notion, and other Markdown editors
  - Full round-trip: Edit in Obsidian → Sync back to app
  - YAML frontmatter includes `type: checklist` for identification

### Fixed

- **� Markdown Parsing Robustness**
  - Fixed content extraction after title (was returning empty for some formats)
  - Now handles single newline after title (was requiring double newline)
  - Protection: Skips import if parsed content is empty but local has content

- **📂 Duplicate Filename Handling**
  - Notes with identical titles now get unique Markdown filenames
  - Format: `title_shortid.md` (e.g., `test_71540ca9.md`)
  - Prevents data loss from filename collisions

- **🔔 Notification UX**
  - No sync notifications when app is in foreground
  - User sees changes directly in UI - no redundant notification
  - Background syncs still show notifications as expected

### Privacy Improvements

- **🔒 WiFi Permissions Removed**
  - Removed `ACCESS_WIFI_STATE` permission
  - Removed `CHANGE_WIFI_STATE` permission
  - WiFi binding now works via IP detection instead of SSID matching
  - Cleaned up all SSID-related code from codebase and documentation

### Technical Improvements

- **📦 New Data Model**
  - `NoteType` enum: `TEXT`, `CHECKLIST`
  - `ChecklistItem` data class with id, text, isChecked, order
  - `Note.kt` extended with `noteType` and `checklistItems` fields

- **🔄 Sync Protocol v1.4.0**
  - JSON format updated to include checklist fields
  - Full backward compatibility with v1.3.x notes
  - Robust JSON parsing with manual field extraction

---

## [1.3.2] - 2026-01-10

### Changed
- **🧹 Code-Qualität: "Clean Slate" Release**
  - Alle einfachen Lint-Issues behoben (Phase 1-7 des Cleanup-Plans)
  - Unused Imports und Members entfernt
  - Magic Numbers durch benannte Konstanten ersetzt
  - SwallowedExceptions mit Logger.w() versehen
  - MaxLineLength-Verstöße reformatiert
  - ConstructorParameterNaming (snake_case → camelCase mit @SerializedName)
  - Custom Exceptions: SyncException.kt und ValidationException.kt erstellt

### Added  
- **📝 F-Droid Privacy Notice**
  - Datenschutz-Hinweis für die Datei-Logging-Funktion
  - Erklärt dass Logs nur lokal gespeichert werden
  - Erfüllt F-Droid Opt-in Consent-Anforderungen

### Technical Improvements
- **⚡ Neue Konstanten für bessere Wartbarkeit**
  - `SYNC_COMPLETED_DELAY_MS`, `ERROR_DISPLAY_DELAY_MS` (MainActivity)
  - `CONNECTION_TIMEOUT_MS` (SettingsActivity)
  - `SOCKET_TIMEOUT_MS`, `MAX_FILENAME_LENGTH`, `ETAG_PREVIEW_LENGTH` (WebDavSyncService)
  - `AUTO_CANCEL_TIMEOUT_MS` (NotificationHelper)
  - RFC 1918 IP-Range Konstanten (UrlValidator)
  - `DAYS_THRESHOLD`, `TRUNCATE_SUFFIX_LENGTH` (Extensions)

- **🔒 @Suppress Annotations für legitime Patterns**
  - ReturnCount: Frühe Returns für Validierung sind idiomatisch
  - LoopWithTooManyJumpStatements: Komplexe Sync-Logik dokumentiert

### Notes
- Komplexe Refactorings (LargeClass, LongMethod) für v1.3.3+ geplant
- Deprecation-Warnungen (LocalBroadcastManager, ProgressDialog) bleiben bestehen

---

## [1.3.1] - 2026-01-08

### Fixed
- **🔧 Multi-Device JSON Sync (Danke an Thomas aus Bielefeld)**
  - JSON-Dateien werden jetzt korrekt zwischen Geräten synchronisiert
  - Funktioniert auch ohne aktiviertes Markdown
  - Hybrid-Optimierung: Server-Timestamp (Primary) + E-Tag (Secondary) Checks
  - E-Tag wird nach Upload gecached um Re-Download zu vermeiden

### Performance Improvements
- **⚡ JSON Sync Performance-Parität**
  - JSON-Sync erreicht jetzt gleiche Performance wie Markdown (~2-3 Sekunden)
  - Timestamp-basierte Skip-Logik für unveränderte Dateien (~500ms pro Datei gespart)
  - E-Tag-Matching als Fallback für Dateien die seit letztem Sync modifiziert wurden
  - **Beispiel:** 24 Dateien von 12-14s auf ~2.7s reduziert (keine Änderungen)

- **⏭️ Skip unveränderte Dateien** (Haupt-Performance-Fix!)
  - JSON-Dateien: Überspringt alle Notizen, die seit letztem Sync nicht geändert wurden
  - Markdown-Dateien: Überspringt unveränderte MD-Dateien basierend auf Server-Timestamp
  - **Spart ~500ms pro Datei** bei Nextcloud (~20 Dateien = 10 Sekunden gespart!)
  - Von 21 Sekunden Sync-Zeit auf 2-3 Sekunden reduziert

- **⚡ Session-Caching für WebDAV** 
  - Sardine-Client wird pro Sync-Session wiederverwendet (~600ms gespart)
  - WiFi-IP-Adresse wird gecacht statt bei jeder Anfrage neu ermittelt (~300ms gespart)
  - `/notes/` Ordner-Existenz wird nur einmal pro Sync geprüft (~500ms gespart)
  - **Gesamt: ~1.4 Sekunden zusätzlich gespart**

- **📝 Content-basierte Markdown-Erkennung**
  - Extern bearbeitete Markdown-Dateien werden auch erkannt wenn YAML-Timestamp nicht aktualisiert wurde
  - Löst das Problem: Obsidian/Texteditor-Änderungen wurden nicht importiert
  - Hybridansatz: Erst Timestamp-Check (schnell), dann Content-Vergleich (zuverlässig)

### Added
- **🔄 Sync-Status-Anzeige (UI)**
  - Sichtbares Banner "Synchronisiere..." mit ProgressBar während Sync läuft
  - Sync-Button und Pull-to-Refresh werden deaktiviert während Sync aktiv
  - Verhindert versehentliche Doppel-Syncs durch visuelle Rückmeldung
  - Auch in Einstellungen: "Jetzt synchronisieren" Button wird deaktiviert

### Fixed
- **🔧 Sync-Mutex verhindert doppelte Syncs**
  - Keine doppelten Toast-Nachrichten mehr bei schnellem Pull-to-Refresh
  - Concurrent Sync-Requests werden korrekt blockiert

- **🐛 Lint-Fehler behoben**
  - `View.generateViewId()` statt hardcodierte IDs in RadioButtons
  - `app:tint` statt `android:tint` für AppCompat-Kompatibilität

### Added
- **🔍 detekt Code-Analyse**
  - Statische Code-Analyse mit detekt 1.23.4 integriert
  - Pragmatische Konfiguration für Sync-intensive Codebasis
  - 91 Issues identifiziert (als Baseline für v1.4.0)

- **🏗️ Debug Build mit separatem Package**
  - Debug-APK kann parallel zur Release-Version installiert werden
  - Package: `dev.dettmer.simplenotes.debug` (Debug) vs `dev.dettmer.simplenotes` (Release)
  - App-Name zeigt "Simple Notes (Debug)" für einfache Unterscheidung

- **📊 Debug-Logging UI**
  - Neuer "Debug Log" Button in Einstellungen → Erweitert
  - Zeigt letzte Sync-Logs mit Zeitstempeln
  - Export-Funktion für Fehlerberichte

### Technical
- `WebDavSyncService`: Hybrid-Optimierung für JSON-Downloads (Timestamp PRIMARY, E-Tag SECONDARY)
- `WebDavSyncService`: E-Tag refresh nach Upload statt Invalidierung (verhindert Re-Download)
- E-Tag Caching: `SharedPreferences` mit Key-Pattern `etag_json_{noteId}`
- Skip-Logik: `if (serverModified <= lastSync) skip` → ~1ms pro Datei
- Fallback E-Tag: `if (serverETag == cachedETag) skip` → für Dateien modifiziert nach lastSync
- PROPFIND nach PUT: Fetch E-Tag nach Upload für korrektes Caching
- `SyncStateManager`: Neuer Singleton mit `StateFlow<Boolean>` für Sync-Status
- `MainActivity`: Observer auf `SyncStateManager.isSyncing` für UI-Updates
- Layout: `sync_status_banner` mit `ProgressBar` + `TextView`
- `WebDavSyncService`: Skip-Logik für unveränderte JSON/MD Dateien basierend auf `lastSyncTimestamp`
- `WebDavSyncService`: Neue Session-Cache-Variablen (`sessionSardine`, `sessionWifiAddress`, `notesDirEnsured`)
- `getOrCreateSardine()`: Cached Sardine-Client mit automatischer Credentials-Konfiguration
- `getOrCacheWiFiAddress()`: WiFi-Adresse wird nur einmal pro Sync ermittelt
- `clearSessionCache()`: Aufräumen am Ende jeder Sync-Session
- `ensureNotesDirectoryExists()`: Cached Directory-Check
- Content-basierter Import: Vergleicht MD-Content mit lokaler Note wenn Timestamps gleich
- Build-Tooling: detekt aktiviert, ktlint vorbereitet (deaktiviert wegen Parser-Problemen)
- Debug BuildType: `applicationIdSuffix = ".debug"`, `versionNameSuffix = "-debug"`

---

## [1.3.0] - 2026-01-07

### Added
- **🚀 Multi-Device Sync** (Thanks to Thomas from Bielefeld for reporting!)
  - Automatic download of new notes from other devices
  - Deletion tracking prevents "zombie notes" (deleted notes don't come back)
  - Smart cleanup: Re-created notes (newer timestamp) are downloaded
  - Works with all devices: v1.2.0, v1.2.1, v1.2.2, and v1.3.0

- **🗑️ Server Deletion via Swipe Gesture**
  - Swipe left on notes to delete from server (requires confirmation)
  - Prevents duplicate notes on other devices
  - Works with deletion tracking system
  - Material Design confirmation dialog

- **⚡ E-Tag Performance Optimization**
  - Smart server checking with E-Tag caching (~150ms vs 3000ms for "no changes")
  - 20x faster when server has no updates
  - E-Tag hybrid approach: E-Tag for JSON (fast), timestamp for Markdown (reliable)
  - Battery-friendly with minimal server requests

- **📥 Markdown Auto-Sync Toggle**
  - NEW: Unified Auto-Sync toggle in Settings (replaces separate Export/Auto-Import toggles)
  - When enabled: Notes export to Markdown AND import changes automatically
  - When disabled: Manual sync button appears for on-demand synchronization
  - Performance: Auto-Sync OFF = 0ms overhead

- **🔘 Manual Markdown Sync Button**
  - Manual sync button for performance-conscious users
  - Shows import/export counts after completion
  - Only visible when Auto-Sync is disabled
  - On-demand synchronization (~150-200ms only when triggered)

- **⚙️ Server-Restore Modes**
  - MERGE: Keep local notes + add server notes
  - REPLACE: Delete all local + download from server
  - OVERWRITE: Update duplicates, keep non-duplicates
  - Restore modes now work correctly for WebDAV restore

### Technical
- New `DeletionTracker` model with JSON persistence
- `NotesStorage`: Added deletion tracking methods
- `WebDavSyncService.hasUnsyncedChanges()`: Intelligent server checks with E-Tag caching
- `WebDavSyncService.downloadRemoteNotes()`: Deletion-aware downloads
- `WebDavSyncService.restoreFromServer()`: Support for restore modes
- `WebDavSyncService.deleteNoteFromServer()`: Server deletion with YAML frontmatter scanning
- `WebDavSyncService.importMarkdownFiles()`: Automatic Markdown import during sync
- `WebDavSyncService.manualMarkdownSync()`: Manual sync with result counts
- `MainActivity.setupSwipeToDelete()`: Two-stage swipe deletion with confirmation
- E-Tag caching in SharedPreferences for performance

---

## [1.2.2] - 2026-01-06

### Fixed
- **Backward Compatibility for v1.2.0 Users (Critical)**
  - App now reads BOTH old (Root) AND new (`/notes/`) folder structures
  - Users upgrading from v1.2.0 no longer lose their existing notes
  - Server-Restore now finds notes from v1.2.0 stored in Root folder
  - Automatic deduplication prevents loading the same note twice
  - Graceful error handling if Root folder is not accessible

### Technical
- `WebDavSyncService.downloadRemoteNotes()` - Dual-mode download (Root + /notes/)
- `WebDavSyncService.restoreFromServer()` - Now uses dual-mode download
- Migration happens naturally: new uploads go to `/notes/`, old notes stay readable

---

## [1.2.1] - 2026-01-05

### Fixed
- **Markdown Initial Export Bugfix**
  - Existing notes are now exported as Markdown when Desktop Integration is activated
  - Previously, only new notes created after activation were exported
  - Progress dialog shows export status with current/total counter
  - Error handling for network issues during export
  - Individual note failures don't abort the entire export

- **Markdown Directory Structure Fix**
  - Markdown files now correctly land in `/notes-md/` folder
  - Smart URL detection supports both Root-URL and `/notes` URL structures
  - Previously, MD files were incorrectly placed in the root directory
  - Markdown import now finds files correctly

- **JSON URL Normalization**
  - Simplified server configuration: enter only base URL (e.g., `http://server:8080/`)
  - App automatically creates `/notes/` for JSON files and `/notes-md/` for Markdown
  - Smart detection: both `http://server:8080/` and `http://server:8080/notes/` work correctly
  - Backward compatible: existing setups with `/notes` in URL continue to work
  - No migration required for existing users

### Changed
- **Markdown Directory Creation**
  - `notes-md/` folder is now created on first sync (regardless of Desktop Integration setting)
  - Prevents 404 errors when mounting WebDAV folder
  - Better user experience: folder is visible before enabling the feature

- **Settings UI Improvements**
  - Updated example URL from `/webdav` to `/notes` to match app behavior
  - Example now shows: `http://192.168.0.188:8080/notes`

### Technical
- `WebDavSyncService.ensureMarkdownDirectoryExists()` - Creates MD folder early
- `WebDavSyncService.getMarkdownUrl()` - Smart URL detection for both structures
- `WebDavSyncService.exportAllNotesToMarkdown()` - Exports all local notes with progress callback
- `SettingsActivity.onMarkdownExportToggled()` - Triggers initial export with ProgressDialog

---

## [1.2.0] - 2026-01-04

### Added
- **Local Backup System**
  - Export all notes as JSON file to any location (Downloads, SD card, cloud folder)
  - Import backup with 3 modes: Merge, Replace, or Overwrite duplicates
  - Automatic safety backup created before every restore
  - Backup validation (format and version check)
  
- **Markdown Desktop Integration**
  - Optional Markdown export parallel to JSON sync
  - `.md` files synced to `notes-md/` folder on WebDAV
  - YAML frontmatter with `id`, `created`, `updated`, `device`
  - Manual import button to pull Markdown changes from server
  - Last-Write-Wins conflict resolution via timestamps
  
- **Settings UI Extensions**
  - New "Backup & Restore" section with local + server restore
  - New "Desktop Integration" section with Markdown toggle
  - Universal restore dialog with radio button mode selection

### Changed
- **Server Restore Behavior**: Users now choose restore mode (Merge/Replace/Overwrite) instead of hard-coded replace-all

### Technical
- `BackupManager.kt` - Complete backup/restore logic
- `Note.toMarkdown()` / `Note.fromMarkdown()` - Markdown conversion with YAML frontmatter
- `WebDavSyncService` - Extended for dual-format sync (JSON master + Markdown mirror)
- ISO8601 timestamp formatting for desktop compatibility
- Filename sanitization for safe Markdown file names

### Documentation
- Added WebDAV mount instructions (Windows, macOS, Linux)
- Complete sync architecture documentation
- Desktop integration analysis

---

## [1.1.2] - 2025-12-28

### Fixed
- **"Job was cancelled" Error**
  - Fixed coroutine cancellation in sync worker
  - Proper error handling for interrupted syncs
  
- **UI Improvements**
  - Back arrow instead of X in note editor (better UX)
  - Pull-to-refresh for manual sync trigger
  - HTTP/HTTPS protocol selection with radio buttons
  - Inline error display (no toast spam)
  
- **Performance & Battery**
  - Sync only on actual changes (saves battery)
  - Auto-save notifications removed
  - 24-hour server offline warning instead of instant error
  
### Changed
- Settings grouped into "Auto-Sync" and "Sync Interval" sections
- HTTP only allowed for local networks (RFC 1918 IPs)
- Swipe-to-delete without UI flicker

---

## [1.1.1] - 2025-12-27

### Fixed
- **WiFi Connect Sync**
  - No error notifications in foreign WiFi networks
  - Server reachability check before sync (2s timeout)
  - Silent abort when server offline
  - Pre-check waits until network is ready
  - No errors during network initialization
  
### Changed
- **Notifications**
  - Old sync notifications cleared on app start
  - Error notifications auto-dismiss after 30 seconds
  
### UI
- Sync icon only shown when sync is configured
- Swipe-to-delete without flicker
- Scroll to top after saving note

### Technical
- Server check with 2-second timeout before sync attempts
- Network readiness check in WiFi connect trigger
- Notification cleanup on MainActivity.onCreate()

---

## [1.1.0] - 2025-12-26

### Added
- **Configurable Sync Intervals**
  - User choice: 15, 30, or 60 minutes
  - Real-world battery impact displayed (15min: ~0.8%/day, 30min: ~0.4%/day, 60min: ~0.2%/day)
  - Radio button selection in settings
  - Doze Mode optimization (syncs batched in maintenance windows)
  
- **About Section**
  - App version from BuildConfig
  - Links to GitHub repository and developer profile
  - MIT license information
  - Material 3 card design

### Changed
- Settings UI redesigned with grouped sections
- Periodic sync updated dynamically when interval changes
- WorkManager uses selected interval for background sync

### Removed
- Debug/Logs section from settings (cleaner UI)

### Technical
- `PREF_SYNC_INTERVAL_MINUTES` preference key
- NetworkMonitor reads interval from SharedPreferences
- `ExistingPeriodicWorkPolicy.UPDATE` for live interval changes

---

## [1.0.0] - 2025-12-25

### Added
- Initial release
- WebDAV synchronization
- Note creation, editing, deletion
- 6 sync triggers:
  - Periodic sync (configurable interval)
  - App start sync
  - WiFi connect sync
  - Manual sync (menu button)
  - Pull-to-refresh
  - Settings "Sync Now" button
- Material 3 design
- Light/Dark theme support
- F-Droid compatible (100% FOSS)

---

[1.8.1]: https://github.com/inventory69/simple-notes-sync/releases/tag/v1.8.1
[1.8.0]: https://github.com/inventory69/simple-notes-sync/releases/tag/v1.8.0
[1.7.2]: https://github.com/inventory69/simple-notes-sync/releases/tag/v1.7.2
[1.7.1]: https://github.com/inventory69/simple-notes-sync/releases/tag/v1.7.1
[1.7.0]: https://github.com/inventory69/simple-notes-sync/releases/tag/v1.7.0
[1.6.1]: https://github.com/inventory69/simple-notes-sync/releases/tag/v1.6.1
[1.6.0]: https://github.com/inventory69/simple-notes-sync/releases/tag/v1.6.0
[1.5.0]: https://github.com/inventory69/simple-notes-sync/releases/tag/v1.5.0
[1.4.1]: https://github.com/inventory69/simple-notes-sync/releases/tag/v1.4.1
[1.4.0]: https://github.com/inventory69/simple-notes-sync/releases/tag/v1.4.0
[1.3.2]: https://github.com/inventory69/simple-notes-sync/releases/tag/v1.3.2
[1.3.1]: https://github.com/inventory69/simple-notes-sync/releases/tag/v1.3.1
[1.3.0]: https://github.com/inventory69/simple-notes-sync/releases/tag/v1.3.0
[1.2.2]: https://github.com/inventory69/simple-notes-sync/releases/tag/v1.2.2
[1.2.1]: https://github.com/inventory69/simple-notes-sync/releases/tag/v1.2.1
[1.2.0]: https://github.com/inventory69/simple-notes-sync/releases/tag/v1.2.0
[1.1.2]: https://github.com/inventory69/simple-notes-sync/releases/tag/v1.1.2
[1.1.1]: https://github.com/inventory69/simple-notes-sync/releases/tag/v1.1.1
[1.1.0]: https://github.com/inventory69/simple-notes-sync/releases/tag/v1.1.0
[1.0.0]: https://github.com/inventory69/simple-notes-sync/releases/tag/v1.0.0