import SwiftUI struct PostTextEditingView: View { @EnvironmentObject var model: WriteFreelyModel @ObservedObject var post: WFAPost @Binding var updatingFromServer: Bool @State private var appearance: PostAppearance = .serif @State private var combinedText = "" @State private var hasBeenEdited: Bool = false let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect() var body: some View { ZStack(alignment: .topLeading) { if combinedText.count == 0 { Text("Write…") .foregroundColor(Color(NSColor.placeholderTextColor)) .padding(.horizontal, 16) .padding(.vertical, 16) .font(.custom(appearance.rawValue, size: 17, relativeTo: .body)) } if post.appearance == "sans" { MacEditorTextView( text: $combinedText, isFirstResponder: combinedText.isEmpty, isEditable: true, font: NSFont(name: PostAppearance.sans.rawValue, size: 17), onEditingChanged: onEditingChanged, onCommit: onCommit, onTextChange: onTextChange ) } else if post.appearance == "wrap" || post.appearance == "mono" || post.appearance == "code" { MacEditorTextView( text: $combinedText, isFirstResponder: combinedText.isEmpty, isEditable: true, font: NSFont(name: PostAppearance.mono.rawValue, size: 17), onEditingChanged: onEditingChanged, onCommit: onCommit, onTextChange: onTextChange ) } else { MacEditorTextView( text: $combinedText, isFirstResponder: combinedText.isEmpty, isEditable: true, font: NSFont(name: PostAppearance.serif.rawValue, size: 17), onEditingChanged: onEditingChanged, onCommit: onCommit, onTextChange: onTextChange ) } } .background(Color(NSColor.controlBackgroundColor)) .onAppear(perform: { if post.title.isEmpty { self.combinedText = post.body } else { self.combinedText = "# \(post.title)\n\n\(post.body)" } }) .onReceive(timer) { _ in if !post.body.isEmpty && hasBeenEdited { DispatchQueue.main.async { LocalStorageManager.standard.saveContext() hasBeenEdited = false } } } } private func onEditingChanged() { hasBeenEdited = true } private func onTextChange(_ text: String) { extractTitle(text) if !updatingFromServer { if post.status == PostStatus.published.rawValue { post.status = PostStatus.edited.rawValue } if post.status == PostStatus.edited.rawValue, post.title == model.editor.initialPostTitle, post.body == model.editor.initialPostBody { post.status = PostStatus.published.rawValue } } if updatingFromServer { self.updatingFromServer = false } hasBeenEdited = true } private func onCommit() { if !post.body.isEmpty && hasBeenEdited { DispatchQueue.main.async { LocalStorageManager.standard.saveContext() } } hasBeenEdited = false } private func extractTitle(_ text: String) { var detectedTitle: String if text.hasPrefix("# ") { let endOfTitleIndex = text.firstIndex(of: "\n") ?? text.endIndex detectedTitle = String(text[..