Fix deletions not sticking (git sync resurrection), persist folder

collapse state, make merge provider picker visibly clickable

Deleted conversations coming back: exportAllConversations() only ever
wrote files for conversations that currently exist — it never removed
the exported markdown file for a conversation that had been deleted
locally. That file just sits in the sync repo forever, so every
future pull+import (including on every app startup) silently
resurrects it, since importAllConversations() only skips an import
when a matching local ID already exists. Fixed by having export also
delete orphaned files (conversation ID no longer present locally), and
added GitSyncService.syncAfterDeletion() — a debounced export+push
triggered right after any delete/bulk-delete/merge-cleanup, so the
removal reaches the remote promptly instead of waiting on an
unrelated future auto-save. Existing duplicates need one more manual
delete to clear, but they'll stay gone after that.

Folder collapse state now persists (SettingsService.collapsedFolderIds,
JSON-encoded like favoriteModelIds) and is restored on app launch, in
both the sidebar and the advanced conversation list.

Merge model picker: the provider switcher was legitimate (it does load
each provider's own catalog independently) but looked like plain
text — no chevron, no button styling — so it wasn't obviously
clickable. Restyled to match HeaderView's provider menu affordance
(icon + label + chevron on a colored pill).
This commit is contained in:
2026-07-28 15:21:11 +02:00
parent 37734232f5
commit 72540305ad
8 changed files with 135 additions and 7 deletions
+21
View File
@@ -515,6 +515,27 @@ class SettingsService {
}
}
// MARK: - Folder Collapse State
/// IDs of folders currently collapsed in the sidebar/conversation list persisted so the
/// app reopens with folders in the same expanded/collapsed state the user left them in.
var collapsedFolderIds: Set<UUID> {
get {
guard let json = cache["collapsedFolderIds"],
let data = json.data(using: .utf8),
let ids = try? JSONDecoder().decode([String].self, from: data) else { return [] }
return Set(ids.compactMap { UUID(uuidString: $0) })
}
set {
let sorted = newValue.map { $0.uuidString }.sorted()
if let data = try? JSONEncoder().encode(sorted),
let json = String(data: data, encoding: .utf8) {
cache["collapsedFolderIds"] = json
DatabaseService.shared.setSetting(key: "collapsedFolderIds", value: json)
}
}
}
/// ISO8601 timestamp of the last local change to favoriteModelIds used to
/// resolve last-write-wins conflicts when syncing favorites across machines.
var favoriteModelsUpdatedAt: String {