merge integration
This commit is contained in:
commit
db1a0aaefa
|
|
@ -3,12 +3,22 @@ import Combine
|
|||
import SwiftUI
|
||||
import UniformTypeIdentifiers
|
||||
|
||||
class WindowController {
|
||||
let window: NSWindow
|
||||
let appState: AppState
|
||||
init(window: NSWindow, appState: AppState) {
|
||||
self.window = window
|
||||
self.appState = appState
|
||||
}
|
||||
}
|
||||
|
||||
class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
var window: NSWindow!
|
||||
var appState: AppState!
|
||||
private var titleCancellable: AnyCancellable?
|
||||
private var titleBarView: TitleBarView?
|
||||
private var focusTitleObserver: NSObjectProtocol?
|
||||
private var windowControllers: [WindowController] = []
|
||||
|
||||
func applicationDidFinishLaunching(_ notification: Notification) {
|
||||
_ = ConfigManager.shared
|
||||
|
|
@ -99,9 +109,14 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||
let item = NSMenuItem()
|
||||
let menu = NSMenu(title: "File")
|
||||
|
||||
let newItem = NSMenuItem(title: "New Note", action: #selector(newNote), keyEquivalent: "n")
|
||||
newItem.target = self
|
||||
menu.addItem(newItem)
|
||||
let newWindowItem = NSMenuItem(title: "New Window", action: #selector(newWindow), keyEquivalent: "n")
|
||||
newWindowItem.target = self
|
||||
menu.addItem(newWindowItem)
|
||||
|
||||
let newNoteItem = NSMenuItem(title: "New Note", action: #selector(newNote), keyEquivalent: "N")
|
||||
newNoteItem.keyEquivalentModifierMask = [.command, .shift]
|
||||
newNoteItem.target = self
|
||||
menu.addItem(newNoteItem)
|
||||
|
||||
let openItem = NSMenuItem(title: "Open...", action: #selector(openNote), keyEquivalent: "o")
|
||||
openItem.target = self
|
||||
|
|
@ -117,6 +132,12 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||
saveAsItem.target = self
|
||||
menu.addItem(saveAsItem)
|
||||
|
||||
menu.addItem(.separator())
|
||||
|
||||
let openStorageItem = NSMenuItem(title: "Open Storage Directory", action: #selector(openStorageDirectory), keyEquivalent: "")
|
||||
openStorageItem.target = self
|
||||
menu.addItem(openStorageItem)
|
||||
|
||||
item.submenu = menu
|
||||
return item
|
||||
}
|
||||
|
|
@ -133,6 +154,26 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||
menu.addItem(withTitle: "Select All", action: #selector(NSText.selectAll(_:)), keyEquivalent: "a")
|
||||
menu.addItem(.separator())
|
||||
|
||||
let boldItem = NSMenuItem(title: "Bold", action: #selector(boldSelection), keyEquivalent: "b")
|
||||
boldItem.target = self
|
||||
menu.addItem(boldItem)
|
||||
|
||||
let italicItem = NSMenuItem(title: "Italic", action: #selector(italicizeSelection), keyEquivalent: "i")
|
||||
italicItem.target = self
|
||||
menu.addItem(italicItem)
|
||||
|
||||
menu.addItem(.separator())
|
||||
|
||||
let tableItem = NSMenuItem(title: "Insert Table", action: #selector(insertTable), keyEquivalent: "t")
|
||||
tableItem.target = self
|
||||
menu.addItem(tableItem)
|
||||
|
||||
let evalItem = NSMenuItem(title: "Smart Eval", action: #selector(smartEval), keyEquivalent: "e")
|
||||
evalItem.target = self
|
||||
menu.addItem(evalItem)
|
||||
|
||||
menu.addItem(.separator())
|
||||
|
||||
let findItem = NSMenuItem(title: "Find...", action: #selector(NSTextView.performFindPanelAction(_:)), keyEquivalent: "f")
|
||||
findItem.tag = Int(NSTextFinder.Action.showFindInterface.rawValue)
|
||||
menu.addItem(findItem)
|
||||
|
|
@ -190,6 +231,51 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||
appState.newNote()
|
||||
}
|
||||
|
||||
@objc private func newWindow() {
|
||||
let state = AppState()
|
||||
let contentView = ContentView(state: state)
|
||||
let hostingView = NSHostingView(rootView: contentView)
|
||||
|
||||
let win = NSWindow(
|
||||
contentRect: NSRect(x: 0, y: 0, width: 1200, height: 800),
|
||||
styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
|
||||
backing: .buffered,
|
||||
defer: false
|
||||
)
|
||||
win.titlebarAppearsTransparent = true
|
||||
win.titleVisibility = .hidden
|
||||
win.backgroundColor = Theme.current.base
|
||||
win.title = "Swiftly"
|
||||
win.contentView = hostingView
|
||||
win.center()
|
||||
win.makeKeyAndOrderFront(nil)
|
||||
|
||||
let controller = WindowController(window: win, appState: state)
|
||||
windowControllers.append(controller)
|
||||
}
|
||||
|
||||
@objc private func openStorageDirectory() {
|
||||
let dir = ConfigManager.shared.autoSaveDirectory
|
||||
let url = URL(fileURLWithPath: dir, isDirectory: true)
|
||||
NSWorkspace.shared.open(url)
|
||||
}
|
||||
|
||||
@objc private func boldSelection() {
|
||||
NotificationCenter.default.post(name: .boldSelection, object: nil)
|
||||
}
|
||||
|
||||
@objc private func italicizeSelection() {
|
||||
NotificationCenter.default.post(name: .italicizeSelection, object: nil)
|
||||
}
|
||||
|
||||
@objc private func insertTable() {
|
||||
NotificationCenter.default.post(name: .insertTable, object: nil)
|
||||
}
|
||||
|
||||
@objc private func smartEval() {
|
||||
NotificationCenter.default.post(name: .smartEval, object: nil)
|
||||
}
|
||||
|
||||
@objc private func openNote() {
|
||||
let panel = NSOpenPanel()
|
||||
panel.allowedContentTypes = Self.supportedContentTypes
|
||||
|
|
|
|||
|
|
@ -31,4 +31,8 @@ extension Notification.Name {
|
|||
static let focusEditor = Notification.Name("focusEditor")
|
||||
static let focusTitle = Notification.Name("focusTitle")
|
||||
static let formatDocument = Notification.Name("formatDocument")
|
||||
static let insertTable = Notification.Name("insertTable")
|
||||
static let boldSelection = Notification.Name("boldSelection")
|
||||
static let italicizeSelection = Notification.Name("italicizeSelection")
|
||||
static let smartEval = Notification.Name("smartEval")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1262,26 +1262,25 @@ struct EditorTextView: NSViewRepresentable {
|
|||
private var isUpdatingImages = false
|
||||
private var isUpdatingTables = false
|
||||
private var embeddedTableViews: [MarkdownTableView] = []
|
||||
private var focusObserver: NSObjectProtocol?
|
||||
private var settingsObserver: NSObjectProtocol?
|
||||
private var observers: [NSObjectProtocol] = []
|
||||
|
||||
init(_ parent: EditorTextView) {
|
||||
self.parent = parent
|
||||
super.init()
|
||||
focusObserver = NotificationCenter.default.addObserver(
|
||||
|
||||
observers.append(NotificationCenter.default.addObserver(
|
||||
forName: .focusEditor, object: nil, queue: .main
|
||||
) { [weak self] _ in
|
||||
guard let tv = self?.textView else { return }
|
||||
tv.window?.makeFirstResponder(tv)
|
||||
tv.setSelectedRange(NSRange(location: 0, length: 0))
|
||||
}
|
||||
NotificationCenter.default.addObserver(
|
||||
})
|
||||
observers.append(NotificationCenter.default.addObserver(
|
||||
forName: .formatDocument, object: nil, queue: .main
|
||||
) { [weak self] _ in
|
||||
self?.formatCurrentDocument()
|
||||
}
|
||||
|
||||
settingsObserver = NotificationCenter.default.addObserver(
|
||||
})
|
||||
observers.append(NotificationCenter.default.addObserver(
|
||||
forName: .settingsChanged, object: nil, queue: .main
|
||||
) { [weak self] _ in
|
||||
guard let tv = self?.textView, let ts = tv.textStorage else { return }
|
||||
|
|
@ -1297,14 +1296,31 @@ struct EditorTextView: NSViewRepresentable {
|
|||
applySyntaxHighlighting(to: ts, format: parent.fileFormat)
|
||||
ts.endEditing()
|
||||
tv.needsDisplay = true
|
||||
}
|
||||
})
|
||||
observers.append(NotificationCenter.default.addObserver(
|
||||
forName: .boldSelection, object: nil, queue: .main
|
||||
) { [weak self] _ in
|
||||
self?.wrapSelection(with: "**")
|
||||
})
|
||||
observers.append(NotificationCenter.default.addObserver(
|
||||
forName: .italicizeSelection, object: nil, queue: .main
|
||||
) { [weak self] _ in
|
||||
self?.wrapSelection(with: "*")
|
||||
})
|
||||
observers.append(NotificationCenter.default.addObserver(
|
||||
forName: .insertTable, object: nil, queue: .main
|
||||
) { [weak self] _ in
|
||||
self?.insertBlankTable()
|
||||
})
|
||||
observers.append(NotificationCenter.default.addObserver(
|
||||
forName: .smartEval, object: nil, queue: .main
|
||||
) { [weak self] _ in
|
||||
self?.performSmartEval()
|
||||
})
|
||||
}
|
||||
|
||||
deinit {
|
||||
if let obs = focusObserver {
|
||||
NotificationCenter.default.removeObserver(obs)
|
||||
}
|
||||
if let obs = settingsObserver {
|
||||
for obs in observers {
|
||||
NotificationCenter.default.removeObserver(obs)
|
||||
}
|
||||
}
|
||||
|
|
@ -1497,6 +1513,47 @@ struct EditorTextView: NSViewRepresentable {
|
|||
textView.insertText("\n" + indent, replacementRange: textView.selectedRange())
|
||||
}
|
||||
|
||||
private func wrapSelection(with wrapper: String) {
|
||||
guard let tv = textView else { return }
|
||||
let sel = tv.selectedRange()
|
||||
guard sel.length > 0 else { return }
|
||||
let str = tv.string as NSString
|
||||
let selected = str.substring(with: sel)
|
||||
let wrapped = wrapper + selected + wrapper
|
||||
tv.insertText(wrapped, replacementRange: sel)
|
||||
tv.setSelectedRange(NSRange(location: sel.location + wrapper.count, length: selected.count))
|
||||
}
|
||||
|
||||
private func insertBlankTable() {
|
||||
guard let tv = textView else { return }
|
||||
let table = "| Column 1 | Column 2 | Column 3 |\n| --- | --- | --- |\n| | | |\n"
|
||||
tv.insertText(table, replacementRange: tv.selectedRange())
|
||||
}
|
||||
|
||||
private func performSmartEval() {
|
||||
guard let tv = textView else { return }
|
||||
let str = tv.string as NSString
|
||||
let cursor = tv.selectedRange().location
|
||||
let lineRange = str.lineRange(for: NSRange(location: cursor, length: 0))
|
||||
let line = str.substring(with: lineRange).trimmingCharacters(in: .newlines)
|
||||
let trimmed = line.trimmingCharacters(in: .whitespaces)
|
||||
|
||||
if trimmed.hasPrefix("let ") {
|
||||
if let eqIdx = trimmed.firstIndex(of: "="), eqIdx > trimmed.startIndex {
|
||||
let afterLet = trimmed[trimmed.index(trimmed.startIndex, offsetBy: 4)..<eqIdx]
|
||||
.trimmingCharacters(in: .whitespaces)
|
||||
let insertion = "\n/= \(afterLet)\n"
|
||||
let endOfLine = NSMaxRange(lineRange)
|
||||
tv.insertText(insertion, replacementRange: NSRange(location: endOfLine, length: 0))
|
||||
}
|
||||
} else if !trimmed.isEmpty {
|
||||
let lineStart = lineRange.location
|
||||
let whitespacePrefix = line.prefix(while: { $0 == " " || $0 == "\t" })
|
||||
let insertLoc = lineStart + whitespacePrefix.count
|
||||
tv.insertText("/= ", replacementRange: NSRange(location: insertLoc, length: 0))
|
||||
}
|
||||
}
|
||||
|
||||
func textView(_ textView: NSTextView, clickedOnLink link: Any, at charIndex: Int) -> Bool {
|
||||
var urlString: String?
|
||||
if let url = link as? URL {
|
||||
|
|
@ -1946,7 +2003,7 @@ private func highlightMarkdownLine(_ trimmed: String, line: String, lineRange: N
|
|||
let contentStart = hashRange.location + hashRange.length
|
||||
let contentRange = NSRange(location: contentStart, length: NSMaxRange(lineRange) - contentStart)
|
||||
if contentRange.length > 0 {
|
||||
let h3Font = NSFont.systemFont(ofSize: 15, weight: .bold)
|
||||
let h3Font = NSFont.systemFont(ofSize: round(baseFont.pointSize * 1.15), weight: .bold)
|
||||
textStorage.addAttribute(.font, value: h3Font, range: contentRange)
|
||||
textStorage.addAttribute(.foregroundColor, value: palette.text, range: contentRange)
|
||||
}
|
||||
|
|
@ -1961,7 +2018,7 @@ private func highlightMarkdownLine(_ trimmed: String, line: String, lineRange: N
|
|||
let contentStart = hashRange.location + hashRange.length
|
||||
let contentRange = NSRange(location: contentStart, length: NSMaxRange(lineRange) - contentStart)
|
||||
if contentRange.length > 0 {
|
||||
let h2Font = NSFont.systemFont(ofSize: 18, weight: .bold)
|
||||
let h2Font = NSFont.systemFont(ofSize: round(baseFont.pointSize * 1.38), weight: .bold)
|
||||
textStorage.addAttribute(.font, value: h2Font, range: contentRange)
|
||||
textStorage.addAttribute(.foregroundColor, value: palette.text, range: contentRange)
|
||||
}
|
||||
|
|
@ -1976,7 +2033,7 @@ private func highlightMarkdownLine(_ trimmed: String, line: String, lineRange: N
|
|||
let contentStart = hashRange.location + hashRange.length
|
||||
let contentRange = NSRange(location: contentStart, length: NSMaxRange(lineRange) - contentStart)
|
||||
if contentRange.length > 0 {
|
||||
let h1Font = NSFont.systemFont(ofSize: 22, weight: .bold)
|
||||
let h1Font = NSFont.systemFont(ofSize: round(baseFont.pointSize * 1.69), weight: .bold)
|
||||
textStorage.addAttribute(.font, value: h1Font, range: contentRange)
|
||||
textStorage.addAttribute(.foregroundColor, value: palette.text, range: contentRange)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue