import SwiftUI import Combine import UniformTypeIdentifiers class NotepadDocument: ObservableObject { @Published var text: String = "" @Published var currentFileURL: URL? = nil @Published var encoding: String.Encoding = .utf8 func save(isSaveAs: Bool = false) { if isSaveAs || currentFileURL == nil { let panel = NSSavePanel() panel.allowedContentTypes = [.plainText] if panel.runModal() == .OK { currentFileURL = panel.url } else { return } } guard let url = currentFileURL else { return } do { try text.write(to: url, atomically: true, encoding: encoding) } catch { DispatchQueue.main.async { let alert = NSAlert() alert.messageText = "儲存失敗" alert.informativeText = error.localizedDescription alert.runModal() } } } func open() { let panel = NSOpenPanel() panel.allowedContentTypes = [.plainText] if panel.runModal() == .OK, let url = panel.url { do { if let content = try? String(contentsOf: url, encoding: .utf8) { self.text = content self.encoding = .utf8 } else { self.text = try String(contentsOf: url, encoding: .windowsCP1252) self.encoding = .windowsCP1252 } self.currentFileURL = url } catch { print("讀取失敗") } } } } struct ContentView: View { @ObservedObject var doc: NotepadDocument @State private var fontSize: CGFloat = 16 @State private var currentTheme: ColorScheme? = .dark var body: some View { VStack(spacing: 0) { TextEditor(text: $doc.text) .font(.system(size: fontSize)) .scrollContentBackground(.hidden) .background(currentTheme == .dark ? Color.black : Color.white) .padding(5) Divider() HStack { Text(doc.currentFileURL?.lastPathComponent ?? "新文件") Spacer() Text("縮放: \(Int((fontSize/16)*100))%") } .padding(.horizontal, 10) .font(.system(size: 11)) .frame(height: 22) .background(Color.secondary.opacity(0.1)) } .preferredColorScheme(currentTheme) .frame(minWidth: 800, minHeight: 600) .onAppear { NSEvent.addLocalMonitorForEvents(matching: .keyDown) { event in if event.modifierFlags.contains(.command) { switch event.characters { case "+", "=": fontSize += 2; return nil case "-": fontSize = max(6, fontSize - 2); return nil case "0": fontSize = 16; return nil default: break } } return event } } } }