feat: add note formatting keyboard shortcuts

This commit is contained in:
2026-10-19 20:28:44 +02:00
parent 04bfd785e7
commit c53ad3515a
4 changed files with 126 additions and 11 deletions
+34 -6
View File
@@ -22,6 +22,12 @@ final class NotesController: NSViewController, NSTextViewDelegate {
private let status = NSTextField(labelWithString: "")
private let noteTitle = NSTextField(labelWithString: "Untitled Note")
private let formatting = NSStackView()
private static let formattingKeys: [(key: String, modifiers: NSEvent.ModifierFlags)] = [
("h", [.command, .shift]), ("b", .command), ("i", .command),
("x", [.command, .shift]), ("e", .command), ("k", [.command, .shift]),
("e", [.command, .shift]), ("b", [.command, .shift]),
("o", [.command, .shift]), ("u", [.command, .shift]), ("t", [.command, .shift]),
]
private let defaults: UserDefaults
private var store: NotesStore?
private var selectedID: UUID?
@@ -86,6 +92,8 @@ final class NotesController: NSViewController, NSTextViewDelegate {
for (index, tool) in tools.enumerated() {
let button = iconButton(tool.0, label: tool.1, action: #selector(formatNote(_:)))
button.tag = index
let shortcut = Self.formattingKeys[index]
button.toolTip = "\(tool.1) (⌘\(shortcut.modifiers.contains(.shift) ? "⇧" : "")\(shortcut.key.uppercased()))"
formatting.addArrangedSubview(button)
}
formatting.spacing = 8
@@ -210,9 +218,16 @@ final class NotesController: NSViewController, NSTextViewDelegate {
if let (open, close) = wrappers[sender.tag] {
let before = selection.location - open.utf16.count
let after = NSMaxRange(selection)
var stars = 0
if open == "*" {
while selection.location > stars, after + stars < source.length,
source.character(at: selection.location - stars - 1) == 42,
source.character(at: after + stars) == 42 { stars += 1 }
}
if before >= 0, after + close.utf16.count <= source.length,
source.substring(with: NSRange(location: before, length: open.utf16.count)) == open,
source.substring(with: NSRange(location: after, length: close.utf16.count)) == close {
source.substring(with: NSRange(location: after, length: close.utf16.count)) == close,
open != "*" || stars % 2 == 1 {
let body = contents.attributedSubstring(from: selection)
selection = NSRange(location: before, length: selection.length + open.utf16.count + close.utf16.count)
replaceMarkdown(body, range: selection, selection: NSRange(location: before, length: body.length))
@@ -479,8 +494,17 @@ final class NotesController: NSViewController, NSTextViewDelegate {
return true
}
func handleCommandKey(_ key: String) -> Bool {
guard store != nil else { return false }
func handleCommandKey(_ event: NSEvent) -> Bool {
guard store != nil, view.window?.attachedSheet == nil else { return false }
let modifiers = event.modifierFlags.intersection([.command, .control, .option, .shift])
let key = event.charactersIgnoringModifiers?.lowercased() ?? ""
if let index = Self.formattingKeys.firstIndex(where: { $0.key == key && $0.modifiers == modifiers }) {
guard editor.isEditable, !actions.isShown, view.window?.firstResponder === editor,
let button = formatting.arrangedSubviews[index] as? NSButton else { return false }
formatNote(button)
return true
}
guard modifiers == .command else { return false }
switch key {
case "p": browseNotes()
case "k": showActions()
@@ -583,11 +607,10 @@ final class NotesController: NSViewController, NSTextViewDelegate {
@MainActor
private final class NotesView: NSView {
var commandShortcut: ((String) -> Bool)?
var commandShortcut: ((NSEvent) -> Bool)?
override func performKeyEquivalent(with event: NSEvent) -> Bool {
if event.modifierFlags.intersection([.command, .control, .option, .shift]) == .command,
commandShortcut?(event.charactersIgnoringModifiers?.lowercased() ?? "") == true {
if commandShortcut?(event) == true {
return true
}
return super.performKeyEquivalent(with: event)
@@ -682,6 +705,11 @@ enum MarkdownHighlighting {
guard !blocks.contains(where: { NSIntersectionRange($0.range, match.range).length > 0 }),
!codeSpans.contains(where: { NSIntersectionRange($0.range, match.range).length > 0 }) else { continue }
storage.addAttributes(attributes, range: match.range)
if match.range(withName: "marker").length == 3,
let font = attributes[.font] as? NSFont {
storage.addAttribute(.font, value: NSFontManager.shared.convert(font, toHaveTrait: .italicFontMask),
range: match.range)
}
hideSyntax(match)
}
}