Add optional sync between Macs with a shared 8-digit code

Notes and photos were stuck on one machine. A small Go/Postgres backend
stores each group's payload under a code that doubles as the shared
secret, so a second Mac just needs the code — no account or login.

Applying a remote change must not push it straight back, otherwise the
two Macs would bump the version back and forth forever, so payloads are
fingerprinted and identical ones are skipped.

💘 Generated with Crush

Assisted-by: Crush:kimi-k3
This commit is contained in:
2026-10-08 09:56:08 +08:00
parent 4934ad916a
commit 15ad9f355c
11 changed files with 747 additions and 3 deletions
+36 -1
View File
@@ -47,7 +47,7 @@ final class Store: ObservableObject {
try? FileManager.default.createDirectory(at: imagesDirectory, withIntermediateDirectories: true)
load()
if sections.isEmpty {
let workspace = Section(name: "Workspace")
let workspace = Section(name: "Default")
sections = [workspace]
activeSectionID = workspace.id
save()
@@ -77,6 +77,41 @@ final class Store: ObservableObject {
let file = StoreFile(sections: sections, activeSectionID: activeSectionID)
guard let data = try? JSONEncoder().encode(file) else { return }
try? data.write(to: storeFile, options: .atomic)
onDidChange?()
}
// MARK: - Sync support
/// Called after every save so `SyncService` can push the change.
var onDidChange: (() -> Void)?
func replaceAll(sections newSections: [Section], activeSectionID newActiveSectionID: UUID?) {
sections = newSections
if let newActiveSectionID, newSections.contains(where: { $0.id == newActiveSectionID }) {
activeSectionID = newActiveSectionID
} else {
activeSectionID = newSections.first?.id
}
save()
}
func imageData(for filename: String) -> Data? {
try? Data(contentsOf: imagesDirectory.appendingPathComponent(filename))
}
func importImage(filename: String, data: Data) {
let url = imagesDirectory.appendingPathComponent(filename)
if let existing = try? Data(contentsOf: url), existing == data { return }
try? data.write(to: url)
imageCache.removeObject(forKey: filename as NSString)
}
func cleanupOrphanedImages(referenced: Set<String>) {
let files = (try? FileManager.default.contentsOfDirectory(at: imagesDirectory, includingPropertiesForKeys: nil)) ?? []
for file in files where !referenced.contains(file.lastPathComponent) {
try? FileManager.default.removeItem(at: file)
imageCache.removeObject(forKey: file.lastPathComponent as NSString)
}
}
// MARK: - Sections