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
+4 -4
View File
@@ -68,7 +68,7 @@ struct SyncStatus: Equatable {
var remoteStatus: String? // "up-to-date", "ahead 3", "behind 2", etc.
}
struct ConversationExport {
nonisolated struct ConversationExport {
let id: String
let name: String
let createdAt: Date
@@ -76,7 +76,7 @@ struct ConversationExport {
let primaryModel: String? // Primary model used in conversation
let messages: [MessageExport]
struct MessageExport {
nonisolated struct MessageExport {
let role: String
let content: String
let timestamp: Date
@@ -85,7 +85,7 @@ struct ConversationExport {
let modelId: String? // Model that generated this message
}
func toMarkdown() -> String {
nonisolated func toMarkdown() -> String {
var md = "# \(name)\n\n"
md += "**ID**: `\(id)`\n"
md += "**Created**: \(ISO8601DateFormatter().string(from: createdAt))\n"
@@ -129,7 +129,7 @@ struct ConversationExport {
}
/// Parse markdown back to ConversationExport
static func fromMarkdown(_ markdown: String) throws -> ConversationExport {
nonisolated static func fromMarkdown(_ markdown: String) throws -> ConversationExport {
let lines = markdown.components(separatedBy: .newlines)
var lineIndex = 0