diff --git a/src/AppDelegate.swift b/src/AppDelegate.swift index 5b9f79f..06650f3 100644 --- a/src/AppDelegate.swift +++ b/src/AppDelegate.swift @@ -43,6 +43,11 @@ class AppDelegate: NSObject, NSApplicationDelegate { ) } + func application(_ application: NSApplication, open urls: [URL]) { + guard let url = urls.first else { return } + appState.loadNoteFromFile(url) + } + func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { return true } @@ -169,7 +174,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { @objc private func openNote() { let panel = NSOpenPanel() - panel.allowedContentTypes = [UTType(filenameExtension: "md")!, .plainText] + panel.allowedContentTypes = Self.supportedContentTypes panel.canChooseFiles = true panel.canChooseDirectories = false panel.allowsMultipleSelection = false @@ -180,13 +185,21 @@ class AppDelegate: NSObject, NSApplicationDelegate { } @objc private func saveNote() { - appState.saveNote() + if appState.currentFileURL != nil { + appState.saveNote() + } else { + saveNoteAs() + } } @objc private func saveNoteAs() { let panel = NSSavePanel() - panel.allowedContentTypes = [UTType(filenameExtension: "md")!] + panel.allowedContentTypes = Self.supportedContentTypes panel.nameFieldStringValue = defaultFilename() + if let url = appState.currentFileURL { + panel.directoryURL = url.deletingLastPathComponent() + panel.nameFieldStringValue = url.lastPathComponent + } panel.beginSheetModal(for: window) { [weak self] response in guard response == .OK, let url = panel.url else { return } self?.appState.saveNoteToFile(url) @@ -194,6 +207,9 @@ class AppDelegate: NSObject, NSApplicationDelegate { } private func defaultFilename() -> String { + if let url = appState.currentFileURL { + return url.lastPathComponent + } let firstLine = appState.documentText .components(separatedBy: "\n").first? .trimmingCharacters(in: .whitespaces) ?? "" @@ -201,11 +217,74 @@ class AppDelegate: NSObject, NSApplicationDelegate { of: "^#+\\s*", with: "", options: .regularExpression ) let trimmed = stripped.trimmingCharacters(in: .whitespaces) - guard !trimmed.isEmpty, trimmed != "Untitled" else { return "note.md" } + let ext = extensionForFormat(appState.currentFileFormat) + guard !trimmed.isEmpty, trimmed != "Untitled" else { return "note.\(ext)" } let sanitized = trimmed.map { "/:\\\\".contains($0) ? "-" : String($0) }.joined() - return sanitized.prefix(80) + ".md" + return sanitized.prefix(80) + ".\(ext)" } + private func extensionForFormat(_ format: FileFormat) -> String { + switch format { + case .markdown: return "md" + case .csv: return "csv" + case .json: return "json" + case .toml: return "toml" + case .yaml: return "yaml" + case .xml: return "xml" + case .svg: return "svg" + case .rust: return "rs" + case .c: return "c" + case .cpp: return "cpp" + case .objc: return "m" + case .javascript: return "js" + case .typescript: return "ts" + case .jsx: return "jsx" + case .tsx: return "tsx" + case .html: return "html" + case .css: return "css" + case .scss: return "scss" + case .less: return "less" + case .python: return "py" + case .go: return "go" + case .ruby: return "rb" + case .php: return "php" + case .lua: return "lua" + case .shell: return "sh" + case .java: return "java" + case .kotlin: return "kt" + case .swift: return "swift" + case .zig: return "zig" + case .sql: return "sql" + case .makefile: return "mk" + case .dockerfile: return "Dockerfile" + case .config: return "conf" + case .lock: return "lock" + case .plainText, .unknown: return "txt" + } + } + + private static let supportedContentTypes: [UTType] = { + let extensions = [ + "md", "markdown", "mdown", + "csv", "json", "toml", "yaml", "yml", "xml", "svg", + "rs", "c", "cpp", "cc", "cxx", "h", "hpp", "hxx", + "js", "jsx", "ts", "tsx", + "html", "htm", "css", "scss", "less", + "py", "go", "rb", "php", "lua", + "sh", "bash", "zsh", "fish", + "java", "kt", "kts", "swift", "zig", "sql", + "mk", "ini", "cfg", "conf", "env", + "lock", "txt", "text", "log" + ] + var types: [UTType] = [.plainText] + for ext in extensions { + if let t = UTType(filenameExtension: ext) { + types.append(t) + } + } + return Array(Set(types)) + }() + @objc private func openSettings() { SettingsWindowController.show() }